LeetCode hot 100 — 3. 无重复字符的最长子串

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

示例 1:

复制代码
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。注意 "bca" 和 "cab" 也是正确答案。

示例 2:

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

示例 3:

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

提示:

  • 0 <= s.length <= 105
  • s 由英文字母、数字、符号和空格组成

第一其实是很懵的,很久没写字符串相关的了,本来这一个板块算是我的弱点,而且涉及到的东西也比较多,像动态规划这种。因此先问了GPT,让它给我启发,有些思维模式启发的感觉还是不错。

对算法题的模板感太重了,反而失去了思考。

先别想"该用什么算法模板",先抓住题目中的一个关键词:子串。子串是连续的,所以你实际上是在字符串上维护一段连续区间。

假设你正在从左往右读字符串,并且目前手里有一个满足条件的子串:

复制代码
abc

现在又读到一个 a

复制代码
abca

它不合法了。此时思考三个问题:

  1. 有没有必要把整个子串清空、从新 a 重新开始?
  2. 为了重新合法,应该移动子串的左端还是右端?
  3. 左端最少移动到哪里,才能排除重复的 a

你会发现,不需要全部推倒重来:

复制代码
abca
 ↓
bca

去掉旧的 a 以后,剩下的部分仍然有利用价值。

接下来尝试用纸模拟:

复制代码
pwwkew

每读入一个字符,都记录四件事:

复制代码
当前读到的字符
当前连续区间
区间是否合法
历史最大长度

关键在于始终维持一个"不重复区间"。新字符进来后:

  • 没有重复:区间可以继续扩大。
  • 出现重复:缩小区间,直到它重新合法。
  • 重新合法后:更新最大长度。

你需要维护的状态大致只有:

  • 当前区间的左边界。
  • 正在向右探索的位置。
  • 当前区间中有哪些字符,或者每个字符最近在哪里出现过。
  • 目前见过的最大长度。

根据GPT的引导和回复写了第一版代码:

python 复制代码
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        n = len(s)
        res = 1

        left, right = 0, 1
        substr = {s[0]:0}

        while left < n and right < n:
            if s[right] not in substr:
                substr[s[right]] = right
                right += 1
                res = max(res, len(substr))
            else:
                # 出现了重复的
                index = substr[s[right]] # 取出重复的位置
                left = index + 1
                if right <= left: right = left + 1
                
                substr.clear()
                substr[s[left]] = left
        return res

能过三个简单的测试用例,但提交有 661 / 1036 个通过的测试用例

边界问题,或者说是移动问题,也就是出现重复字符的时候如何更新让它重新变回一个合法区间,给个反例你就能想明白了

输入

s ="mjvhmi"

输出

4

预期结果

5

自己想了下又修改了一版,逻辑没有问题可以通过,但是差点忘记len(s) == 0的情况了

python 复制代码
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 最后一版本!!忘记了边界,注意是可以提交空字串的!!
        if not s:
            return 0
            
        n = len(s)
        res = 1

        left, right = 0, 1
        substr = {s[0]:0}

        while left < n and right < n:
            if s[right] not in substr:
                substr[s[right]] = right
                res = max(res, len(substr))
            else:
                # 出现了重复的
                index = substr[s[right]] # 取出重复的位置

                while left <= index:
                    del substr[s[left]]
                    left += 1
                left = index + 1
                substr[s[right]] = right
                
            right += 1
        return res

最后GPT给了一个简化版本

python 复制代码
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        res = 0
        left = 0
        last_index = {}

        for right, char in enumerate(s):
            if char in last_index and last_index[char] >= left:
                # 移动左边界
                left = last_index[char] + 1
            last_index[char] = right # 更新
            res = max(res, right - left + 1)   # 相当于维护一个左边界

        return res

这里不去记录每个子串出现的位置,只维护一个字典记录每个字符最后一次出现的位置,避免删除操作,比较麻烦

是啊!res可以不用这个字典的长度去求,有字串左右两侧的Index自然可以得到字串的长度!

相关推荐
李白你好3 小时前
Windows 离线 Linux 提权辅助查询工具
linux
Ivanqhz3 小时前
php8 use-def链
算法·php
Lust Dusk3 小时前
记一次Linux应急响应题目解析
linux·运维·服务器·网络·安全·网络安全
evans在进步3 小时前
LeetCode 189 轮转数组:三次反转原地解决,图解 Java 实现
java·算法·leetcode
歌_顿3 小时前
Drawing_To_Model
算法
龍德明宇4 小时前
AI不会疼-龍德明宇
人工智能·算法·大语言模型llm·负主体性·ai存在论
ShineWinsu4 小时前
对于Linux:五种IO模型以及非阻塞IO的详细解析
linux·c++·面试·io·阻塞·非阻塞·fcntl
千千寰宇4 小时前
[Linux/Ollama] Termux : 一款运行在 Android 平台上的开源终端模拟器与 Linux 环境应用、支持在手机上基于Ollama部署LLM
linux
302wanger4 小时前
程序员写 SOP:把脑子里的流程固化下来
算法
科学实验家4 小时前
完全背包
算法