题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
题目解析及优化
-
暴力法
看到题目最先想到的还是暴力法,寻找以每个字符开头的无重复字串字串,取最长长度。
- 从头开始遍历,以每个字符开头寻找;
- 确定字串的开头字符之后,向后遍历,如果下一个字符与已有的串不重复,则添加到子串中,如果有重复或者到结尾,结束此次遍历,更新最大长度。
- 转向寻找以下一个字符开头的无重复字串。
思路很简单,根据第一题中的技巧,当寻找某一元素在某个集合中是否出现,最好用的是map,在这里我们借鉴这种思路,以数组来存储某个元素是否出现过。
代码如下:
class Solution { public: int lengthOfLongestSubstring(string s) { int l = s.length(); int ans = 0; int table[128]; for(int i = 0;i<l;i++){ //每次进入都需要初始化一次 fill(table,table+128,0); table[s[i]] = 1; //将j定义在外部方便记录 int j = i+1; for(;j<l;j++){ if(table[s[j]]==1){ break; } table[s[j]]=1; } //更新答案 if(j-i>ans){ ans=j-i; } } return ans; } }; -
滑动窗口(双指针)
对上面的暴力法解决之后,仔细思考一下,其实里面可能会有很多重复的部分,比如“abcdefaeeee”的 时候,从a开始,最长字串是“abcdef”,然后从b开始,最长的字串是“bcdefa”,其中"bcdef"就判断了两次是否为含重复字符的字串。面对重复计算的子问题,正常的动态规划在这里不好使,当增添一个字符的时候,对已有的判断时会有影响的。

回到上面的例子“abcdefaeeee”中,其实判断a开头和b开头,从a到b的过渡其实只需要更改一下table中的a即可。可以立即从第二个a开始判断开始判断。

代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int l = s.length();
int ans = 0;
int table[128];
//这里只需要初始化一次即可
fill(table,table+128,0);
int i = 0,j = 0;
for(;i<l;i++){
for(;j<l && table[s[j]]==0;j++){
table[s[j]]=1;
}
if(j-i>ans){
ans=j-i;
}
//在滑动窗口移动的时候要将前一位置为0
table[s[i]]=0;
}
return ans;
}
};
心得及总结
- 面对有重复计算的部分时,并且增加一项对前面的判断有影响时,要好好想想这个影响如何去消除就能利用重复计算的部分(例如本题只需修改前一项);
- 对于散列的思想,有时并不需要用map的数据结构,如可以轻易转化成整数对整数(如ascll码等)的可以用一个数组轻松解决。
- 其实简单的题解大多都是从暴力法逐步优化的,可以先想暴力法,再去渐渐优化。
知识补充
-
ascll码的表示范围技巧:
- 26可以表示26字母,如果要同时表示表示大小写,可以用52(虽然大小写ascll不相连,用if判断转化即可)
- 128表示ascll码,256用于扩展ascll码,一般有128足矣。
最长无重复字符子串:暴力法与滑动窗口优化
本文探讨了解决字符串中无重复字符最长子串问题的两种方法:暴力法与滑动窗口技巧。通过实例和代码演示,优化了查找过程,减少重复计算。重点在于理解如何利用数组(散列)避免重复子串查找,并揭示了使用双指针技巧的高效性。
880

被折叠的 条评论
为什么被折叠?



