-
分析:
- 我们可以将所给的长度为n的字符串,对串中的每一个字符,求得以该字符为结尾的最长无重复字符的子串。所有字串长度的最大值,便是要寻找的答案。
- 如果以s[i]为结尾的最长无重复字符的子串起始位置为j,那么当s[i]遍历到下一个位置s[i + 1]时,此时最长无重复字符的子串起始位置J一定在j的右边或者保持j位置不变,一定不会出现在j的左边。可以通过反证法来证明,倘若当s[i]遍历到下一个位置s[i + 1]时,最长无重复字符的子串起始位置J在j的左边,那么在s[i]还没有遍历到s[i + 1]这个字符的时候,最长无重复字符子串的起始位置一定也在j的左边而非j,这就否定了我们原来的假设,得出矛盾,因此命题得证,即:
<font color="#660000">如果以s[i]为结尾的最长无重复字符的子串起始位置为j,那么当s[i]遍历到下一个位置s[i + 1]时,此时最长无重复字符的子串起始位置J一定在j的右边或者保持j位置不变</font><br /> - 因此,这样就将我们算法的时间复杂度降低了一维,在进行j位置的搜索时,直接从上一状态向右搜索即可。
-
代码:
C++代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> hash; // 建立哈希表(使用unordered_map<char,int> int字段默认初始化为0)
int res = 0; // 定义答案
for (int i = 0, j = 0; i < s.size(); i++){
hash[s[i]] ++; // 把s[i + 1]加入哈希表,此时,如果出现了重复字符,那么hash[s[i]]的值会变成2。
while(hash[s[i]] > 1) hash[s[j++]]--;
/*
hash[s[j++]]--;
这句话的意义在于,当我们发现了重复字符s[i]之后,那么从j位置起,将hash[s[j]]的值减1,并且将j的值增加1,直到我们找到了重复的字符串,结束循环。
拿字符串“zuabca”来说,当遍历到第二个‘a’时,此时i=3,j=0,
(1)将hash[s[j]]的值减1,目的是为了删除重复的字符;
(2)将j的值加1(右移),目的是为了计算最长无重复字符子串的长度;
我们会发现,这样进行的话,会将hash['z'], hash['u']的值也给减1了,我们本想将重复字符‘a’的hash值变成1,即hash['a'] == 1,但是此时出现了这样的结果:hash['z'] == hash['u'] == 0,hash['a'] == 1。
这样的话,对我们解题来说,无所谓!因为前面推导出j是单调向右的,不会再向左了,第一个‘a’的左边的hash['z'], hash['u']变成什么样子,我们都不再去用它了。
*/
res = max(res, i - j + 1); // 找到所有子串的最大值,即为最终结果。
}
return res;
}
};
Python代码:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
Hash = dict()
j, res = 0, 0
for i in range(len(s)):
Hash[s[i]] = Hash[s[i]] + 1 if s[i] in Hash else 1
while Hash[s[i]] > 1:
Hash[s[j]] -= 1
j += 1
res = max(res, i - j + 1)
return res
- 总结:
- 本题主要用到了双指针算法+哈希表知识点,通过分析发现首指针j不会向左移动,因此时间复杂度降为。
网友评论