剑指offer 48. 最长不含重复字符的子字符串

剑指offer 48. 最长不含重复字符的子字符串

题目描述:

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
在这里插入图片描述

考察点:

滑动窗口,动态规划,哈希

解题思路:

①滑动窗口:
当前下标的元素在滑动窗口中如果没有出现,就将当前元素插入到滑动窗口中,窗口变大。
如果当前元素在窗口里面出现过,计数不为0,表示窗口内已经有这个元素了,就需要将窗口左边的下标往右移动一个。
再次进入循环,依旧看当前元素在滑动窗口内的计数,从而根据计数决定是扩大窗口,还是将左边的窗口值继续往右移动。
在这里插入图片描述
②动态规划+哈希表:
状态定义:
动态规划列表dp,dp[fast]表示到fast下标为止,不重复的字符个数,
如果到此为止没有重复的:dp[fast]=dp[fast-1]+1,
如果有重复的,dp[fast]=当前的新的不重复字符串长度;
转移方程:
假设右边界为fast,左边界为slow。
①slow=-1,表示没有没有重复字符,dp[fast]=dp[fast-1]+1,最大不重复字符串长度加一
②dp[fast]<fast-slow,表示slow所在下标的字符并没有出现在当前计数范围内,在当前计数范围的左边,因此当前范围内仍没有出现重复的字符,所以dp[fast]=dp[fast]+1,最大不重复字符串长度加一
③dp[fast]>fast-slow,表示slow此时所在的下标对应的字符已经出现到有效计数范围中了,导致右边界fast-左边界slow比上一次还要小,所以dp[fast]=fast-slow,最大计数改变

结合上面三条,总结出转移方程为:
在这里插入图片描述
所以将哈希表和动态规划的思路结合起来如下:

遍历字符串s。
如果出现新的字符没有在哈希表中,就将这个元素插入到哈希表中。
如果遍历到的当前这个元素在哈希表中已经存在了,就更新这个元素在哈希表中的下标,将原来这个元素的换标换成当前这个元素的下标,比如:原来在哈希表中a元素的下标为0,现在遍历到字符串中有重复的字符a,其下标为3,所以将0换为3。

对于有效元素的长度,在哈希表中使用动态规划:
定义两个下标,fast,slow
fast先便利字符串,当出现和当前元素重复的字符时,slow变成第一次出现这个字符的下标,而fast此时在第二次出现这个字符的位置,记录两者之差:tmp=fast-slow
如果tmp<fast-slow,表示此时的slow没有在当前字符计数范围内,结合下面前三块图,即就是slow所在下标并没有在需要计算不重复区间中,因此有效长度在原来基础上+1;
如果tmp>=fast-slow,表示此时的slow已经移动到有效计数区间范围内部,出现了重复元素,结合下图第四块。因此有效长度改变为fast-slow。
ret表示最终返回的最大的不重复字符串长度。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完整代码:

①滑动窗口(C++):

//滑动窗口
class Solution {
public:
    int lengthOfLongestSubstring(string s) 
    {
        int left=0;
        int right=0;
        int res=0;
        set<char>sliding_window;
        while(left<=right&&right<s.size())
        {
            //如果窗口范围内没有重复的,将右边的窗口右移
            if(!sliding_window.count(s[right]))//表示当前右边窗口的内容在这个窗口里没有出现,计数为0,然后取反加进去为真将这个加进窗口
            {
                sliding_window.insert(s[right]);
                right++;
            }
            //否则左边的窗口向右移动
            else
            {
                res=max<int>(res,sliding_window.size());//这里max后面需要加上<int>,我认为是由于sliding_window.size()前面没有定义类型,所以需要强转一下
                sliding_window.erase(s[left]);
                left++;
            }
        }
        return max<int>(res,sliding_window.size());
    }
};

②动态规划+哈希表(java):

//动态规划和哈希表的方法
class Solution {
    public int lengthOfLongestSubstring(String s) {
        //先创建一个哈希表
        Map<Character,Integer> dic=new HashMap<>();
        int tmp=0;//获取两个重复元素之间的差值
        int ret=0;//获取返回的最长元素个数
        for(int i=0;i<s.length();i++)
        {
            int j=dic.getOrDefault(s.charAt(i),-1);//获取指定元素在哈希表中的下标,如果没有就返回-1.
            //更新哈希表的元素,如果有重复的就进行覆盖,如果没有重复的就将元素添加进哈希表中
            dic.put(s.charAt(i),i);
            tmp=i-j>tmp?tmp+1:i-j;
            ret=Math.max(ret,tmp);
        }
        return ret;
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仟各

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值