剑指offer48-最长不含重复字符的子字符串(双指针经典)

问题描述

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1

示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

s.length <= 40000

解题思路(双指针)

我们不妨以示例1中的字符串abcabcbb为例,找出从每一个字符开始的,不包含重复字符的最长子串,那么其中最长的那个字符串即为答案。对于示例一中的字符串,我们列举出这些结果,其中括号中表示选中的字符以及最长的字符串:
以第1个字符a开头的不含重复字符的最长子串为(abc)abcbb
以第2个字符b开头的不含重复字符的最长子串为a(bca)bcbb
以第3个字符c开头的不含重复字符的最长子串为ab(cab)cbb
以第4个字符a开头的不含重复字符的最长子串为abc(abc)bb
以第5个字符b开头的不含重复字符的最长子串为abca(bc)bb
以第6个字符c开头的不含重复字符的最长子串为abcab(cb)b
以第7个字符b开头的不含重复字符的最长子串为abcabc(b)b
以第8个字符b开头的不含重复字符的最长子串为abcabcb(b)

我们可以发现,如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的
由于这种隐式的递增性,我们就可以使用双指针算法来解决
我们定义两个指针i,j,其中指针i表示枚举每个子串的起始位置,而指针j表示不包含重复子串的结束位置。
而判断子串是否包含重复元素我们可以借助Java中的set集合
在每一次循环遍历的过程中,我们会将指针i向后移动一格,相应的前面一个元素将在集合中被去除,紧接着判断以i为起始位置,j+1为结束位置的子串中是否包含重复的字符,如果不包含,那么我们就把j所指向的元素添加到集合中,并让j往后移动一位。一直遇到某个j+1使得子串中包含重复元素或者走到了字符串的尽头为止。在这过程中我们需要每次比较前面遍历过程中所得到的最大长度值与本次遍历过程中所得到的最大长度值。最终就可以得到最长不包含重复字符的子字符串。

实现代码

class Solution {
    
    
    public int lengthOfLongestSubstring(String s) {
    
    
        int result=0;               //结果变量
        int len=s.length();
        Set<Character> sets=new HashSet<Character>();       //set集合用来判断子字符串是否包含重复元素
        int j=-1;           //j指针即结束位置指针初值应该为-1
        for(int i=0;i<len;i++){
    
    
            if(i!=0){
    
                 //每次遍历新的起始位置,都要把前面的元素从集合中移出。
                sets.remove(s.charAt(i-1));
            }
            //向后移动j指针,直到遇到字符串结尾或者将要包含重复元素为止
            while(j+1<len && !sets.contains(s.charAt(j+1))){
    
    
                //当添加下一个元素,仍不包含重复元素时,可以添加进行,并把j指针后移
                sets.add(s.charAt(j+1));
                j++;
            }
            result=Math.max(result,j-i+1);          //更新结果变量
        }
        return result;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39736597/article/details/113803822