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

11 篇文章 0 订阅

题目描述

思路

lastRepeatIndex:表示上一次重复字符的下标
map<char, int> hash: 比如hash[‘a’] = 2,一方面该数据结构可以判断,当前遍历字符之前有没有出现过;另一方面,可以获取如果字符出现过,出现的位置在哪。

定义f数组,f[i] = k 表示str[0-i]序列的最长的不包含重复字符的子字符串的长度为k
动规转移方程:

     		 |   1, i == 0
    		 |   f[i-1] + 1; i >= 1当前字符没有出现过
     f[i] =  |
    		 |   i - hash[str[i]],并更新lastRepeatIndex = i, hash[str[i]] = i; 当前字符出现过,并且在最近出现重复的字符之后
   			 |   i - lastRepeatIndex, hash[str[i]] = i; 当前字符出现过,并且在最近出现重复的字符之前

举个例子

"wobgrovw" 答案为6
index           : 0  1  2  3  4  5  6  7
s[index]        : w  o  b  g  r  o  v  w
f[index]        : 1  2  3  4  5  4  5  6
lastRepeatIndex :-1 -1 -1 -1 -1  1  1  1
hash[s[index]]  : 0  1  2  3  4  5  6  7
起初,从index = 1开始:'o'字符没有重复,并且之前是没有重复字符的,所以f[1]=f[0]+1, lastRepeatIndex不变
后续直到index=5,'o'字符串出现时,此中间过程忽略
直到到'o'字符再次出现时,hash['o']为1,并且hash['o'] > lastRepeatIndex,说明出现重复的位置在上一次重复位置之后,需要调整 ,所以f[index]为index-hash['o'], 并更新hash['o']为当前字符的index,并更新lastRepeatIndex为hash['o']更新前的值.
最后直到'w'字符出现重复,因为hash['w'] < lastRepeatIndex,表示出现重复的位置在上一次重复位置之前,不会对当前结果产生影响, 所以f[index] = index-lastRepeatIndex,不更新lastRepeatIndex。
最后统计f数组中的最大值即可!
当然,也可以简化f数组,优化空间!

代码

int lengthOfLongestSubstring(string str) {
        if(str.empty() || str.size() == 1) return str.size();
        unordered_map<char, int> hash;
        int preLen = 0, //上一次的长度
            nowLen, // 本轮的长度
            res = INT_MIN, //最终结果
            lastRepeatIndex = -1; //上一次重复的下标
        for(size_t index = 0; index < str.size(); ++index){
            if(hash.count(str[index]) == 0 ){ //如果没有重复,就在上一次的基础上加1
                nowLen = preLen + 1;
            }else{ //有重复时,分情况
                if(lastRepeatIndex > hash[str[index]]){ //如果当前重复的字符,在上一次重复字符的前面,那么这一次重复没有作用,完全可以使用nowLen = preLen + 1代替
                    nowLen = index - lastRepeatIndex;
                }else{ 如果当前重复的字符,在上一次重复字符的后面,那么说明要调整长度,和上一次重复的下标值
                    nowLen = index - hash[str[index]];
                    lastRepeatIndex = hash[str[index]];
                }
            }
            hash[str[index]] = index;
            preLen = nowLen;
            res = res > nowLen ? res : nowLen;
        }
        return res;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值