Problem
给定一个字符串,找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb” 输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb” 输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew” 输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
注意: 答案必须是子串的长度,“pwke” 是一个子序列,不是子串。
Solving
滑动窗口法
寻找最长的无重复字符串,我们按照索引顺序从左往右扫描,首先给它一个左边界left,和一个当前扫描位置i,将当前扫描的字符与滑动窗口中的字符串比较,如果不存在,i向右扩展,如果存在,左边界left移到重复字符上次出现位置的下一个位置。每次计算字符串的长度,并与历史最长字符串比较。这样一次遍历之后,我们就会得到了字符串的长度。
代码设计分析
- 需要一个left变量实时跟踪左边界,左边界一开始的位置是什么,当然是从0开始,不过窗口要完全包含不重复的字符串,要从-1开始,这样扫描到第0个字符时,长度1可以直接用下标减去左边界。
- 需要一层for循环去扫描字符。
- 判断当前字符是否已经出现。
- 记录每个字符最后出现的位置,因为在移动左边界时需要记录位置。
- 记录最长长度和当前长度的两个变量。
(3和4可以一起实现,用一个结构去存放每个字符最后出现的位置,如果这个位置比左边界大,即在窗口内,同时将左边界移动。)
方法一: 所有ASCII字符存储
ASCII只有256个字符,我们可以用一个256的数组对应存储每种字符的结果。
方法二: 出现字符存储
将出现过的字符加入到哈希表中,记录每个字符对应最后出现的位置。
Code(c++)
方法一:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left = -1;
int noRepeat = 0;
vector<int> asciimap (256, -1);
for(int i = 0; i < s.length(); i++){
left = max(left, asciimap[s[i]]);
asciimap[s[i]] = i;
noRepeat = max(noRepeat, i - left);
}
return noRepeat;
}
};
执行用时 :8 ms
内存消耗 :10.1 MB
方法二:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left = -1;
int noRepeat = 0;
map<char, int> strmap;
map<char, int>::iterator iter;
for(int i = 0; i < s.length(); i++){
iter = strmap.find(s[i]);
if(iter == strmap.end()){
strmap.insert(pair<char, int>(s[i], i));
}
else{
left = max(left, iter->second);
strmap[s[i]] = i;
}
noRepeat = max(noRepeat, i - left);
}
return noRepeat;
}
};
执行用时 :20 ms
内存消耗 :10.2 MB
从空间复杂度来说,第二种方法理论上所需的空间会更少,但看结果,并没有更好,时间复杂度反而更高。所以对于这个题目,推荐方法一。如果把寻找最长不重复字符串改成寻找数组中最长不重复的数,那第一种方法就无法实现了,但第二种方法却不受影响,可见方法二的鲁棒性更好!