LeetCode3-无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
一、思路
(一)基本想法
一开始的基本想法是用一个循环遍历整个字符串:
设置一个哈希表用于查找当前字符是否重复。
- 不重复,长度+1,继续
- 重复,记录当前长度,并与之前的最大长度进行比较,记录下较长的
随后重新开始计算子串长度
(1)出错1:
最后一次子串的长度没有参与比较导致结果出错
(2)出错2
这个想法有个漏洞:重新开始计算子串长度,在这一步中,由于事先没有记录下上一个子串的起始位置,因此新的子串永远是以重复位置作为起始点来计算长度的,所以会导致结果错误。
(二)改进方法
(1)设置两个循环:
-
外层循环,确定子串的起始位置
在某些条件下可以直接不进入内层循环直接结束
能想到的条件是:当前最长子串的长度 = (字符串结束位置 - 当前子串起始位置) -
内层循环,确定子串的长度
采用(一)的方法即可
(2)C++代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
map<char, int> save_c;
int len = 0, len_max = 0, j;
for (j = 0; j < s.size(); j++) {
if (len_max >= (s.size() - j))
break;
for (int i = j; i < s.size(); i++) {
// 判断当前字符是否重复
if (save_c.count(s[i]) != 0) {
// 发生重复时,清空哈希表,并退出
save_c.clear();
if (len > len_max) {
len_max = len;
}
len = 0;
break;
}
save_c.insert(map<char, int>::value_type(s[i], 1));
len++;
}
if (len > len_max)
len_max = len;
}
return len_max;
}
};
执行效率:
虽然勉强通过了,但效率上来说,被人吊打,嗯。。。果然还是想的太简单了。
(三)再次改进
(1)疑问
实际上,当前子串出现重复时,有必要将之前的哈希表进行清空吗?
不用,假设当前子串的起始位置是i,结束位置是j,可以知道的是第j个字符与第k个字符(k=i,i+1,…,j-1)重复
因此,起始位置在k之前且包含第k个字符的子串长度都不可能超过之前找到的子串的最大长度
所以,我们要做的就是找出这个k,从第k+1个字符作为起始位置,进行重复匹配
(2)C++代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
map<char, int> save_c;
map<char, int>::iterator it;
int len = 0, len_max = 0, j, k;
for (j = 0; j < s.size(); j++) {
if (len_max >= (s.size() - j))
break;
for (int i = j + len; i < s.size(); i++) {
it = save_c.find(s[i]);
// 判断当前字符是否重复
if (it != save_c.end()) {
// 在哈希表中找到了该字符,重复
k = it->second;
// 清除j--k的字符
for (int m = j; m <= k; m++) {
save_c.erase(s[m]);
}
// 判断当前子串长度是否大于之前找到的最大长度
if (len > len_max) {
len_max = len;
}
// 以第k+1个字符作为下一个子串的起始位置,再次进行匹配
len = i - k;
save_c.insert(map<char, int>::value_type(s[i], i));
j = k;
break;
}
save_c.insert(map<char, int>::value_type(s[i], i));
len++;
}
if (len > len_max)
len_max = len;
}
return len_max;
}
};
执行效率:
可以看出比之前快上了十几倍,但是也仅仅击败了60%的人,说明算法还有有待改进
更进一步的改进方法还没有想出了,凭直觉,如果想要更快一步,应该需要换一个思路了,这个就留到第二轮刷题再说了