题目分析
方法一:动态规划
求数组的连续字串相关,第一反应应该是动态规划,其中状态d[i]表示以第i个字符为结尾,无重复字符串的最长字串,状态转移方程:
d[i] = (s[i]是否与d[i-1]中的字符重复?找到重复的位置,d[i]等于两者相减:d[i-1]+1)
问题一:如何判断是否重复?
使用map或set
问题二:如何找到重复的位置?
使用map,其key = 字符,其value=位置。
问题三:需要每个d[i]都配一个map吗?
并不需要,一次遍历,判断d[i-1]的长度时候落在了当前重复字的区间内.
,如果出现新的重复,将其更新即可。
时间复杂度为O(n),空间复杂度为O(n)
方法二:双指针
使用两根起始位置在字符串头的指针i,p2。i先移动,当遇到重复的字母时,开始记录len1 = i-重复处的位置,len2=i-p,用max和len2比较,如果len1>=len2,就另p移动到重复处+1的地方,否则不改变p1的位置。
注意,最后要比较一下最后一个不重复的字符串和当前Max的值。
思考
双指针也比较适和求连续的字串这种问题,但是双指针的初始位置和移动路径应该如何安排,这个一定要想清楚。
代码
第一种方法:
class Solution {
public int lengthOfLongestSubstring(String s) {
int[] d = new int[s.length()+1];
d[0] = 0;
int len = s.length()+1;
//d[i]初始化为1
for(int i = 1;i<len;i++){
d[i] = 1;
}
Map<Character,Integer> map = new HashMap<>();
for(int i = 1;i<len;i++){
char cur = s.charAt(i-1);
if(map.containsKey(cur)){
int tmpLen = i-map.get(cur)-1;
if(tmpLen>d[i-1]){
d[i] = d[i-1]+1;
}
else{
d[i] = tmpLen;
}
}
else{
d[i] = d[i-1]+1;
}
map.put(cur,i-1);
}
int maxLen = 0;
for(int i = 0;i<len;i++){
maxLen=Math.max(d[i],maxLen);
}
return maxLen;
}
}
第二种方法:
class Solution {
public int lengthOfLongestSubstring(String s) {
int p1 = 0;
int p2 = 0;
int max = 0;
Map<Character,Integer> map = new HashMap<>();
for(int i = 0;i<s.length();i++){
char cur = s.charAt(i);
if(map.containsKey(cur)){
int len1 = i-p1;
int len2 = i-map.get(cur);
max = Math.max(max,len1);
if(len2<=len1){
p1=map.get(cur)+1;
}
}
map.put(cur,i);
}
max = Math.max(s.length()-p1,max);
return max;
}
}