LeetCode3 LongestSubstringWithoutRepeatingCharacters
题目描述
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
解答
解法一:滑动窗口_1
基本思路是,利用滑动窗口,移动右边界直到下一个字符x在窗口内出现过,此时记录窗口长度,用以更新所求子串长度,然后移动左边界,可以一个一个移动,也可以一次性移到窗口内字符x出现的位置上。
下面的代码是利用String类里面的基本方法substring和contains。看着很简洁易懂,但是实际上substring 方法容易产生大量字符串对象(只有在返回字符串本身时返回原对象,否则返回真子串时,会new新对象)。此代码是一个一个移动的左边界。
private static int solve1(String s){
int len = s.length();
int l=0,r=0; //子串从[l,r]处
int maxLen=0;
if (len==1)
return len;
while (l<len ){
if(r+1< len && !s.substring(l,r+1).contains(s.charAt(r+1)+"")){
r++;
maxLen = Math.max(maxLen, r-l+1);
}else{
l++;
}
}
return maxLen;
}
解法二:滑动窗口_2
与解法一基本思路一样,只是需要开辟一个额外256个空间的数组(数组大小可以根据题目要求来定,26:a-z或者A-Z; 128:ASCII码,256:扩展的ASCII码)。利用每个字符的ASCII码来定在数组中的索引,存储的值是该字符出现的次数。代码比较简洁,空间也比较小。
private static int solve1_1(String s){
int[] freq=new int[256];
int maxLen =0, len = s.length();
int l=0,r=-1; //子串从[l,r]处
while (l<len){
if (r+1<len && freq[s.charAt(r+1)]==0){
r++;
freq[s.charAt(r)]++;
}else {
freq[s.charAt(l)]--;
l++;
}
maxLen = Math.max(maxLen, r-l+1);
}
return maxLen;
}
解法三:滑动窗口_3(官方题解)
思路上还是滑动窗口基本思路。但是这个就用了优化:当子串s1遇到重复字符x时,我们不需要逐一去更新左指针l的值, 而是直接跳到子串中重复字符x’的位置上,这样就节省了一部分时间。
本代码来自官方题解,大意是利用一个hashMap来存储各个字符的最新出现的索引位置,当出现重复字符时,直接将左边界 i 跳到原索引的后一位,再更新该索引。
public static int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of character
// try to extend the range [i, j]
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;
}
解法四:滑动窗口_4(官方题解)
同样用了滑动窗口优化。本代码来自官方题解,只是将map换成了整型数组。用一个整型数组代替map来存储每个字符的最新出现的索引位置,字符在数组中的位置由ASCII码计算,存储的是最新出现的字符在字符串中的位置索引,当出现重复字符时,直接将i跳到原索引的后一位,再更新该索引。个人觉得比解法三要好一些,毕竟hashmap的存取过程还是需要一些时间的,而数组访问是O(1)。
public int lengthOfLongestSubstring2(String s) {
int n = s.length(), ans = 0;
int[] index = new int[128]; // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
i = Math.max(index[s.charAt(j)], i);
ans = Math.max(ans, j - i + 1);
index[s.charAt(j)] = j + 1;
}
return ans;
}