剑指 Offer 48. 最长不含重复字符的子字符串 || 力扣3. 无重复字符的最长子串

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

 方法一:动态规划+哈希表

//使用动态规划+哈希表
//状态定义:dp[j]为以s[j]字符结尾的最长字符字串的长度,dp[j-1]是以s[j-1]为结尾的字符字串的长度
//转移方程:i是满足s[i]=s[j]的,且最靠近j的角标,现在就需要比较j和i之间的差值,来判断dp[j-1]所囊括的子字符串是否将s[i]包含在内
//         if   dp[j-1]<j-i;说明以s[j]字符结尾的最长字符字串的长度dp[j]应该比dp[j-1]至少大1,dp[j] = dp[j-1]+1
//         else dp[j-1]>=j-i;说明dp[j-1]所囊括的子字符串是否将s[i]包含在内,dp[j] = j-i;
//初始状态:哈希表用来存放{'字符',index},并且需要随时更新,保证index是与j最贴近的
//           i =-1,j =0;dp[0] =1;mp[s[0]] = 0; 

时间复杂度 O(N) : 其中 N 为字符串长度,动态规划需遍历计算 dp 列表。
空间复杂度 O(1) : 字符的 ASCII 码范围为 00 ~ 127127 ,哈希表mp最多使用 O(128) = O(1) 大小的额外空间。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.size();
        if(len<1)return 0;
        int i =-1,j =0;
        vector<int> dp(len+1,0);dp[0]=1;
        unordered_map<char,int> mp;
        mp[s[0]] = 0;
        for(j = 1;j<len;j++){
            //先判断字符s[j] ~~ 'x'是否在哈希表内,如果不在就添加,并将下标添加到second值
            if(mp.find(s[j])==mp.end()){
                mp[s[j]] = j;
                dp[j] = dp[j-1]+1;
            }
            //如果在就先提取出先前的下标,再更新下标添加到second值
            else{
                i= mp[s[j]];
                mp[s[j]] = j;
                dp[j] = dp[j-1]<j-i?dp[j-1]+1:j-i;
            }
        }
        return *max_element(dp.begin(),dp.end());
    }
};

 上述代码使用了哈希表和vector容器,由于返回值是取 dp 列表最大值,因此可借助变量 tmp 存储 dp[j] ,变量 res 每轮更新最大值即可。此优化可节省 dp 列表使用的 O(N)大小的额外空间。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.size();
        if(len<1)return 0;
        int i =0,j =0;
        int tmp =0,res =0;
        //vector<int> dp(len+1,0);dp[0]=1;
        unordered_map<char,int> mp;
        //mp[s[0]] = 0;
        for(j = 0;j<len;j++){
            //先判断字符s[j] ~~ 'x'是否在哈希表内,如果不在就添加,并将下标添加到second值
            if(mp.find(s[j])==mp.end()){
                mp[s[j]] = j;
                tmp+=1;
                if(tmp>res) res =tmp;
            }
            //如果在就先提取出先前的下标,再更新下标添加到second值
            else{
                i= mp[s[j]];
                mp[s[j]] = j;
                tmp = tmp<(j-i)?tmp+1 : j-i;
                if(tmp>res) res =tmp;
            }
        }
        return res;
    }
};
//#########################################################
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> dic;
        int res = 0, tmp = 0, len = s.size(), i;
        for(int j = 0; j < len; j++) {
            if(dic.find(s[j]) == dic.end()) i = - 1;
            else i = dic.find(s[j])->second; // 获取索引 i
            dic[s[j]] = j; // 更新哈希表
            tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
            res = max(res, tmp); // max(dp[j - 1], dp[j])
        }
        return res;
    }
};

 方法二:动态规划+线性遍历

//使用动态规划+线性遍历
//遍历字符s[j],初始化i =j-1,使i向左侧遍历(时间复杂度O(n^1)),直至i = 0


class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int i = 0,j =0,tmp =0,res_max=0;//这里就不再使用另外的空间去存放dp值了,直接取大tmp的即可
        if(s.size()<1) return 0;
        for(j =0;j<s.size();++j){
            i = j-1;
            while(i>=0&&s[i]!=s[j]) i--;
            tmp = tmp<j-i ? tmp+1 : j-i;
            res_max = max(res_max,tmp);
        }  
        return res_max;
    }
};

 方法三:动态规划+哈希表(双指针)

此方法和方法一类似,只是i所指向的位置有了不同的定义,i为左指针,j为右指针,我们需要保证[i+1,j]区间内没有重复的字符

方法是,使用哈希表,随着右指针的遍历(将最新字符及其角标添加到哈希表的同时),左指针i也需要去检查s[j]字符是否再哈希表内,如果在,就直接替换掉其位置,i= mp[s[j]], 之后再更新 mp[s[j]] = j 

这时候,j-i就是这个字符的全长,取最大的即可

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> dic;
        int i = -1, res = 0, len = s.size();
        for(int j = 0; j < len; j++) {  
            if(dic.find(s[j]) != dic.end())
                i = max(i, dic[s[j]]); // 更新左指针
            dic[s[j]] = j; // 哈希表记录
            res = max(res, j - i); // 更新结果
        }
        return res;
    }
};

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

黑化草莓熊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值