问题描述:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
题目链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
解题思路:
这题使用暴力法双层循环遍历所有子串会超时,为了避免超时,使用尺取法。尺取法非常适合用于查找字符串中符合特定要求的子串。**尺取法通常是指保留数组的一对下标(起点和终点),然后根据实际情况交替推进两个端点直到得出答案。**这种操作很像尺取虫的爬行方式,故而得名。
类似的题目有:
代码实现:
import java.util.HashSet;
public class text03无重复字符的最长子串 {
public static void main(String[] args) {
String str = "dvdf";
System.out.println(lengthOfLongestSubstring(str));
}
public static int lengthOfLongestSubstring(String s) {
int res = 0;
char[] arr = s.toCharArray();
int star = 0;
int end = 0;
while(star<=end && end<arr.length){
HashSet<Character> set = new HashSet<Character>();
for(int i=star; i<=end; i++){
set.add(arr[i]);
}
if(set.size()==(end-star+1)){ //无重复
if(set.size()>res){
res = set.size();
}
end++;
}else{ //有重复
star++;
}
}
return res;
}
}
提交结果:
上面的解法可以优化,右指针的遍历不变,左指针的遍历可以进行优化。我们可以在右指针遍历的时候记录字符出现的最后位置。假设右指针当前在的位置是j,当左指针移动时我们可以直接将左指针移动到上一次字符s[j]最后出现的位置i(注意要与左指针原来的位置进行比较,因为字符最后出现的位置不一定更靠后);因为再左指针移动到i位置之前都是会出现重复的,所以可以直接移动到i位置。这样可以加快了左指针的移动速度,减少了重复子串的判断。
代码如下:
public int lengthOfLongestSubstring(String s) {
int res = 0;
char[] arr = s.toCharArray();
Map<Character,Integer> dic = new HashMap<Character, Integer>(); //记录字符最后出现的位置。
//j表示右指针,i表示左指针
int i = -1;
for(int j=0; j<arr.length; j++) {
if(dic.containsKey(arr[j])) {
i = Math.max(dic.get(arr[j]), i); //更细左指针,注意要比较,因为字符最后出现的位置不一定更靠后
}
dic.put(arr[j], j); //记录字符出现的组后位置
res = Math.max(res, j-i);
}
return res;
}
提交结果:
这个优化的方法本质上和动态规划右类似的地方。
参考文章:
双指针和动态规划