【LeetCode】3. 无重复字符的最长子串(同剑指 Offer 48)

一、题目

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

示例 1:

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

示例 2:

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

示例 3:

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

二、解决

1、动态规划+哈希

思路:(不好理解,详细可看参考1中的幻灯片)

1、状态定义
dp[j] : 以字符s[j]为结尾的“最长不重复字符串”的长度。

2、转移方程
固定右边界j,左边界为i,i从-1开始。

设s[i]=s[j],即字符s[j]左边距离最近相同字符为s[i]1)当 i<0,即s[j]左边无相同字符,则dp[j] = dp[j-1] + 1;
2)当dp[j-1] < j-i,(临界值--max{dp[j-1]} = j-i)说明字符s[i]在子字符串dp[j-1] /*区间之外*/,则dp[j] = dp[j-1] + 11&2)可推出:当i<0,由于dp[j-1]<=j恒成立,因为dp[j-1]<j-i恒成立,即顺利合并12)。

3)当dp[j-1]>=j-i, 说明字符s[i]在字符串dp[j-1] /*区间之中*/,则dp[j]左边界由s[i]决定,即dp[j]=j-i.

dp[j] = { dp[j-1]+1,  dp[j-1] <  j-i }
        { j-i,        dp[j-1] >= j-i }

说明

j0123
字符aaba
dp[j]1122
2)当dp[j-1] < j-i,(临界值--max{dp[j-1]} = j-i)说明字符s[i]在子字符串dp[j-1] /*区间之外*/,则dp[j] = dp[j-1] + 1。
  • j = 2,dp[j-1] = dp[2-1] = dp[1] = 1,字符a(第一个a [i = 0])在字符串dp[j-1] = 1(ab [j = 1 && j = 2])的区间之外。
3)当dp[j-1]>=j-i, 说明字符s[i]在字符串dp[j-1] /*区间之中*/,则dp[j]左边界由s[i]决定,即dp[j]=j-i.
  • j = 3, dp[j-1] = dp[2] = 2 >= 3-1,说明字符a(i = 1)在字符串dp[j-1] = dp[2] = {ab [i=1 && j-1=2} 区间之中。
    1

代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int res = 0, curRes = 0;
        HashMap<Character, Integer> dic = new HashMap<>();
        for (int cur = 0; cur < s.length(); cur++) {
            int pre = dic.getOrDefault(s.charAt(cur), -1);  // 获取索引 pre
            dic.put(s.charAt(cur), cur);  // 更新哈希表
            curRes = curRes < cur - pre ? curRes+1 : cur - pre;
            res = Math.max(res, curRes);
        }
        return res;
    }
}

时间复杂度: O ( n ) O(n) O(n),n为字符串长度
空间复杂度: O ( 1 ) O(1) O(1),字符ASCII码范围0~127,最多使用O(128)=O(1)大小的额外空间。

2、双指针+哈希表

思路:

与方法1类似,不同点在于左边界 i 的定义。

  • 哈希表dic统计:指针 j 遍历字符 s,哈希表统计字符 s[j] 最后一次出现的索引
  • 左指针 i:根据上轮左指针 i 和 dic[s[j]],每轮更新左边界 i,保证区间 [i+1, j]内无重复字符且最大。 i = m a x ( d i c ( [ s [ j ] ] , i ) i = max(dic([s[j]], i) i=max(dic([s[j]],i)
  • 更新结果 res:取上轮res 和 本轮双指针区间[i+1, j]的宽度(即j-i)中的最大值。
    r e s = m a x ( r e s , j − i ) res = max(res, j-i) res=max(res,ji)

代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> dic = new HashMap<>();
        int pre = -1, res = 0;
        for(int cur = 0; cur < s.length(); cur++) {
            if(dic.containsKey(s.charAt(cur)))
                pre = Math.max(pre, dic.get(s.charAt(cur))); // 更新左指针 i  最大值:abba,读到第二个a,cur=3时,pre能更新到1,否则pre=0,最后res是3,不对了。
            dic.put(s.charAt(cur), cur); // 哈希表记录
            res = Math.max(res, cur - pre); // 更新结果
        }
        return res;
    }
}

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1) = O ( 128 ) O(128) O(128)

三、参考

1、面试题48. 最长不含重复字符的子字符串(动态规划 / 双指针 + 哈希表,清晰图解)
2、无重复字符的最长子串
3、画解算法:3. 无重复字符的最长子串
4、11-line simple Java solution, O(n) with explanation
5、Share my Java solution using HashSet
6、Shortest O(n) DP solution with explanations

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值