[leetcode] 3. 无重复字符的最长子串

题目描述

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

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

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

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

提示:
0 0 0 <= s.length <= 5 ∗ 1 0 4 5 * 10^4 5104
s 由英文字母、数字、符号和空格组

解题方法

方法一:暴力枚举

根据题目描述,找出不含有重复字符的最长子串的长度,也可以理解成遍历字符串,以遍历到的位置为起点,计算不含有重复字符的最长字符串长度,遍历完成后,取其中的最大值。
如果按照上面说的方法,每遍历到一个位置,就从该位置往后查找,直到发现重复字符,计算不重复字符串长度,那么时间复杂度为 O ( N 2 ) O(N^2) O(N2),空间复杂度为 O ( N ) O(N) O(N)

java代码

public int lengthOfLongestSubstring1(String s) {
    int max = 0;
    for(int i = 0; i < s.length(); i++) {
        Set<Character> set = new HashSet<>();
        int j = i;
        for(; j < s.length(); j++) {
            char a = s.charAt(j);
            if(!set.contains(a)) {
                set.add(a);
            } else {
                break;
            }
        }
        max = Math.max(max, j - i);
    }
    return max;
}

方法二:滑动窗口

那么,方法一有优化的空见吗?答案是有。
我们可以以示例1的字符串为例,从头开始遍历一次发现规律。

  • ( a ) b c a b c b b (a)bcabcbb (a)bcabcbb 开始的最长字符串为 ( a b c ) a b c b b (abc)abcbb (abc)abcbb
  • a ( b ) c a b c b b a(b)cabcbb a(b)cabcbb 开始的最长字符串为 a ( b c a ) b c b b a(bca)bcbb a(bca)bcbb
  • a b ( c ) a b c b b ab(c)abcbb ab(c)abcbb 开始的最长字符串为 a b ( c a b ) c b b ab(cab)cbb ab(cab)cbb
  • a b c ( a ) b c b b abc(a)bcbb abc(a)bcbb 开始的最长字符串为 a b c ( a b c ) b b abc(abc)bb abc(abc)bb
  • a b c a ( b ) c b b abca(b)cbb abca(b)cbb 开始的最长字符串为 a b c a ( b c ) b b abca(bc)bb abca(bc)bb
  • a b c a b ( c ) b b abcab(c)bb abcab(c)bb 开始的最长字符串为 a b c a b ( c b ) b abcab(cb)b abcab(cb)b
  • a b c a b c ( b ) b abcabc(b)b abcabc(b)b 开始的最长字符串为 a b c a b c ( b ) b abcabc(b)b abcabc(b)b
  • a b c a b c b ( b ) abcabcb(b) abcabcb(b) 开始的最长字符串为 a b c a b c b ( b ) abcabcb(b) abcabcb(b)

可以发现递增遍历子串的起始位置时,子串的结束位置也是递增的。当我们以第k个位置为起点时,得到的结束点为 r k r_k rk,而当我们以第k+1个位置为起点时,得到的结束点必然在 r k r_k rk或者 r k r_k rk之后。因为在k到 r k r_k rk之间,不包含重复字符串,那么在k+1到 r k r_k rk之间,必然也不包含重复字符串。如此这般,我们便可以用滑动窗口的思路,设置左指针和右指针,在遍历字符串时依次扩大左右指针的范围,找到最长的不重复子串。

java代码

import java.util.HashMap;
import java.util.Map;

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> map = new HashMap<>();
        int max = 0;
        for (int i = 0, j = 0; j < s.length(); j++) {
            char a = s.charAt(j);
            if (map.containsKey(a)) {
                i = Math.max(i, map.get(a));
            }
            map.put(a, j + 1);
            max = Math.max(max, j - i + 1);
        }
        return max;
    }
}

以上代码中,i代表左指针,j代表右指针。map用来记录其中出现的字符,key代表字符,value代表当前右指针的下一个位置。在遍历的过程中,当我们发现重复字符时,左指针需要移动到map中的value位置。需要注意的是以上代码中map没有删除key,所以当map发现重复字符,而当前左指针i已经大于map中的value时,则i的值保持不变。

时间复杂度: O ( N ) O(N) O(N)
空间复杂度: O ( N ) O(N) O(N)

相似题目

[leetcode] 30. 串联所有单词的子串


  • 个人公众号
    个人公众号
  • 个人小游戏
    个人小游戏
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

会飞的大鱼人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值