LeetCode(3.无重复字符的最长子串[中等])—滑动窗口(双指针)

最长无重复字符子串:暴力法与滑动窗口优化
本文探讨了解决字符串中无重复字符最长子串问题的两种方法:暴力法与滑动窗口技巧。通过实例和代码演示,优化了查找过程,减少重复计算。重点在于理解如何利用数组(散列)避免重复子串查找,并揭示了使用双指针技巧的高效性。

题目

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

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

题目解析及优化

  1. 暴力法

    看到题目最先想到的还是暴力法,寻找以每个字符开头的无重复字串字串,取最长长度。

    • 从头开始遍历,以每个字符开头寻找;
    • 确定字串的开头字符之后,向后遍历,如果下一个字符与已有的串不重复,则添加到子串中,如果有重复或者到结尾,结束此次遍历,更新最大长度。
    • 转向寻找以下一个字符开头的无重复字串。

    思路很简单,根据第一题中的技巧,当寻找某一元素在某个集合中是否出现,最好用的是map,在这里我们借鉴这种思路,以数组来存储某个元素是否出现过。

    代码如下:

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int l = s.length();
            int ans = 0;
            int table[128];
            for(int i = 0;i<l;i++){
                //每次进入都需要初始化一次
                fill(table,table+128,0);
                table[s[i]] = 1;
                //将j定义在外部方便记录
                int j = i+1;
                for(;j<l;j++){
                    if(table[s[j]]==1){
                        break;
                    }
                    table[s[j]]=1;
                }
                //更新答案
                if(j-i>ans){
                    ans=j-i;
                }
            }
            return ans;
        }
    };
    
  2. 滑动窗口(双指针)

    对上面的暴力法解决之后,仔细思考一下,其实里面可能会有很多重复的部分,比如“abcdefaeeee”的 时候,从a开始,最长字串是“abcdef”,然后从b开始,最长的字串是“bcdefa”,其中"bcdef"就判断了两次是否为含重复字符的字串。面对重复计算的子问题,正常的动态规划在这里不好使,当增添一个字符的时候,对已有的判断时会有影响的。

在这里插入图片描述

回到上面的例子“abcdefaeeee”中,其实判断a开头和b开头,从a到b的过渡其实只需要更改一下table中的a即可。可以立即从第二个a开始判断开始判断。

在这里插入图片描述

代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int l = s.length();
        int ans = 0;
        int table[128];
        //这里只需要初始化一次即可
        fill(table,table+128,0);
        int i = 0,j = 0;
        for(;i<l;i++){
            for(;j<l && table[s[j]]==0;j++){
                table[s[j]]=1;
            }
            if(j-i>ans){
                ans=j-i;
            }
            //在滑动窗口移动的时候要将前一位置为0
            table[s[i]]=0;
        }
        return ans;
    }
};

心得及总结

  1. 面对有重复计算的部分时,并且增加一项对前面的判断有影响时,要好好想想这个影响如何去消除就能利用重复计算的部分(例如本题只需修改前一项);
  2. 对于散列的思想,有时并不需要用map的数据结构,如可以轻易转化成整数对整数(如ascll码等)的可以用一个数组轻松解决。
  3. 其实简单的题解大多都是从暴力法逐步优化的,可以先想暴力法,再去渐渐优化。

知识补充

  1. ascll码的表示范围技巧:

    • 26可以表示26字母,如果要同时表示表示大小写,可以用52(虽然大小写ascll不相连,用if判断转化即可)
    • 128表示ascll码,256用于扩展ascll码,一般有128足矣。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值