经典问题,记录一下这道题解法
问题描述
给定一个字符串,找到最长子串的长度,要求子串不含不重复字符。
样例
给定"pwkpwo"的答案是是4("kpwo")。
给定"bbbbb"的答案是1("b")。
解法
1.双指针-滑动窗口
i:表示窗口左端点
j:表示窗口右端点
set存储s[i]到s[j]间的字符
如果没有重复字符,那么将当前窗口大小与目前最长的子串长度比较并更新,j++;
如果有重复字符,那么将窗口左端点右移,i++,直到窗口内不含重复字符,执行上一行
复杂度:
遍历一遍字符串s为O(n),而set里极端情况下也只存128个字符,所以插入删除是O(1)的,总的复杂度就是O(n)
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
// try to extend the range [i, j] if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
set.remove(s.charAt(i++));
}
}
return ans;
}
}
2.改进版滑动窗口
相比于上一种方法,改良的代码里用一个map去存各字符最近出现位置的下一个位置,好处是滑动窗口的左边缘i不用再一步步地挪了,直接一步到位
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map map = new HashMap<>();
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
ans = Math.max(ans, j - i + 1);
map.put(s.charAt(j), j + 1);
}
return ans;
}
}
举个栗子
s="pwkpwo"
(1)最初,map为空,i=j=0,ans=窗口大小=j-i+1=1,更新map,记录'p'最近出现位置的下一个位置为:1
(2)i=0,j=1,窗口内无重复字符,ans=窗口大小=2,记录'w'最近出现位置的下一个位置为:2
(3))i=0,j=2,窗口内无重复字符,ans=窗口大小=3,记录'k'最近出现位置的下一个位置为:3
(4))i=0,j=3,s[j]与先前窗口内字符重复,因此需要将窗口左端点移动到让窗口不含'p'的最小位置(即map.get('p')),更新后i=1,这样s[i]到s[j]间又没有重复字符串了;同时更新'p'最近出现位置的下一个位置为:4
更新i后:
后边就不再解释了
总之这种方法的改进就是在窗口内字符与s[j]重复时,把窗口左端点一步一步挪动改进成了一步到位,但是本质上复杂度还是没有改变,依旧是O(n)
查找最长子串的长度(不重复字符) - linghu_java - 博客园www.cnblogs.com