题目描述
思路解析
一句话答案:LeetCode 2486 追加字符以获得子序列:一个指针 j 顺着 t 走、遍历 s 能对上就让 j 前进,扫完 s 后追加数就是 t 没对上的尾巴 |t| 减 j,时间 O(|s|)、空间 O(1)。
追加最少字符让 t 成为 s 的子序列,求几个
给两个只含小写字母的字符串 s 和 t,允许往 s 的末尾接上任意字符,问最少接几个,能让 t 成为 s 的子序列。子序列 = 保持相对顺序、从原串里删掉若干字符剩下的串,中间可以跳、不必挨着。题面例子:s=coaching、t=coding 答案 4;s=abcde、t=a 答案 0;s=z、t=abcde 答案 5。
为什么算两串长度差就交卷会错
容易把答案当成两个串的长度差,或者以为要在 s 里找出 t 这一整段连续子串。两条都不对:追加数只跟 t 里「在 s 中对不上」的字符有关,s 再长、缺 t 要的字符也白搭;而子序列允许中间跳字符,不要求连续。真去枚举 t 塞进 s 的每种嵌法硬试,方案多到指数级、根本扫不完。
顺着 t 走一遍、能对上就前进
真正省力的办法是盯着 t 的进度:拿一个指针 j 记「t 已顺序对上到第几位」,从头遍历 s。每看 s 的一个字符,只有它正好等于当前要找的 t[j] 时,才说明 t 这一位在 s 里落实了,j 挪一格去找下一位;对不上就跳过这个 s 字符,j 原地不动。这是子序列匹配的标准双指针,并贪心让每个 t 字符尽早对上——选最靠前的合法位置,不挡后面字符的机会,匹配到的位数只多不少。
扫完 s 后 j 停在哪,尾巴为什么就是要追加的
s 扫到头时,j 停的数值就是 t 从开头起能被顺序对上的最长前缀长度。前面这 j 位已稳稳嵌在 s 里,剩下 t 从第 j 位往后那段尾巴,在现有 s 里再找不到落点。可 t 的相对顺序不能改,这段尾巴只能原样接到 s 末尾,正好 |t| 减 j 个。所以扫完直接返回 len(t) 减 j。这也说清两条边界:t 本就是 s 的子序列时 j 走到底、答案 0;一个字符都对不上时 j 停在 0、答案就是整个 |t|。
顺着 coaching 和 coding 走一遍
记 n=len(t)=6,匹配指针 j 从 0 出发,逐个扫 s=coaching。s[0]=c 等于 t[0]=c,j 进到 1;s[1]=o 等于 t[1]=o,j 进到 2;s[2]=a 要找 t[2]=d,不等、跳过,j 停 2;后面 c、h、i、n、g 一路都不是 d,j 始终卡在 2。s 扫完时 j 落在 2,返回 6 减 2 得 4,正是往末尾追加 ding 这四个字符。
另两组:s=z、t=abcde,z 不等于 t[0]=a,j 一直是 0,返回 5 减 0 得 5,整个 t 都得追加;反过来 s=abcde、t=a,第一个 a 就对上 t[0],j 变 1,返回 1 减 1 得 0,本来就是子序列,一个都不用加。
一趟线性扫,这几处最容易写岔
时间 O(|s|):s 扫一遍、每个字符一次常数比较,j 最多前进 |t| 次;空间 O(1),只用 j 和一个长度变量。几处手滑得防:匹配不上时若也让 j 前进,等于跳过没对上的字符、答案算小;相等时忘了继续挪 s 的下标,遇到 t 里连续相同字符会拿同一个 s 字符重复顶两位;还有别把答案写成 len(s) 减 len(t),跟 s 的长度无关。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记牢这一句:一个指针 j 顺着 t 走,在 s 里从左到右能对上就前进,对不上就跳过;s 扫完 j 停在哪,答案就是 t 的长度减去 j。下面每一帧都在套这句话。
先把两个串摆好。上面这一排是 s,coaching,一共 8 个字符,我们要在它里面从左到右顺序匹配。右边这一列是目标串 t,coding,共 6 个字符,谁被匹配到就打勾。现在还一个都没开始,匹配进度 j 是 0,高亮的是 t 的第一个字符 c,那是我们最先要在 s 里找的。
指针就位。i 指向 s 的第 0 个字符,也就是 c;j 指向 t 的第 0 个字符,也是 c。接下来的规则很简单,拿 s[i] 和 t[j] 比:相等就说明 t 的这个字符在 s 里对上了,j 前进;不相等就只把 s 往后挪,j 原地不动。
扫到 s 的第 0个字符 c。现在 j 是 0,要找的 t[0] 是 c。把这两个放一起比一比:c 和 c 一样吗?
一样!c 正好是要找的 t 字符,命中。把 s 的这一格标绿,j 前进到 1,下一个要在 s 里找的目标变成 t[1] 等于 o。已经顺次对上 1 个了。
扫到 s 的第 1个字符 o。现在 j 是 1,要找的 t[1] 是 o。把这两个放一起比一比:o 和 o 一样吗?
一样!o 正好是要找的 t 字符,命中。把 s 的这一格标绿,j 前进到 2,下一个要在 s 里找的目标变成 t[2] 等于 d。已经顺次对上 2 个了。
扫到 s 的第 2个字符 a。现在 j 是 2,要找的 t[2] 是 d。把这两个放一起比一比:a 和 d 一样吗?
不一样。a 不是当前要找的 d,这个字符对匹配没用,直接跳过。注意 j 一点不动,还是 2,我们只是把 s 往后挪一格继续看。要找的目标依旧是 t[2] 等于 d。
扫到 s 的第 3个字符 c。现在 j 是 2,要找的 t[2] 是 d。把这两个放一起比一比:c 和 d 一样吗?
不一样。c 不是当前要找的 d,这个字符对匹配没用,直接跳过。注意 j 一点不动,还是 2,我们只是把 s 往后挪一格继续看。要找的目标依旧是 t[2] 等于 d。
扫到 s 的第 4个字符 h。现在 j 是 2,要找的 t[2] 是 d。把这两个放一起比一比:h 和 d 一样吗?
不一样。h 不是当前要找的 d,这个字符对匹配没用,直接跳过。注意 j 一点不动,还是 2,我们只是把 s 往后挪一格继续看。要找的目标依旧是 t[2] 等于 d。
扫到 s 的第 5个字符 i。现在 j 是 2,要找的 t[2] 是 d。把这两个放一起比一比:i 和 d 一样吗?
不一样。i 不是当前要找的 d,这个字符对匹配没用,直接跳过。注意 j 一点不动,还是 2,我们只是把 s 往后挪一格继续看。要找的目标依旧是 t[2] 等于 d。
扫到 s 的第 6个字符 n。现在 j 是 2,要找的 t[2] 是 d。把这两个放一起比一比:n 和 d 一样吗?
不一样。n 不是当前要找的 d,这个字符对匹配没用,直接跳过。注意 j 一点不动,还是 2,我们只是把 s 往后挪一格继续看。要找的目标依旧是 t[2] 等于 d。
扫到 s 的第 7个字符 g。现在 j 是 2,要找的 t[2] 是 d。把这两个放一起比一比:g 和 d 一样吗?
不一样。g 不是当前要找的 d,这个字符对匹配没用,直接跳过。注意 j 一点不动,还是 2,我们只是把 s 往后挪一格继续看。要找的目标依旧是 t[2] 等于 d。
s 的 8 个字符全部扫完了。回头看 j,它停在 2。这说明 t 的前 2 个字符,也就是 co,已经能在 s 里按顺序找到(看那两个绿格)。可是 j 没能走到底,t 后面还剩字符没匹配上。
看看 t 里没打勾的部分:ding,一共 4 个字符。它们在 s 里已经没有机会再顺序补齐了,因为 s 已经扫到头。想让 t 成为子序列,只能把这 ding 原样接到 s 的末尾。
答案出来了。t 的长度是 6,已经顺序匹配上 2 个,还差 6 减 2 等于 4 个。这 4 就是要往 s 末尾追加的最少字符数,追加后 s 变成 coachingding,t 就成了它的子序列。
边界想清:t 本就是子序列答案 0、一个都对不上答案就是 |t|、中间对上一半就追加剩下的。
面试重点:是子序列判定的升级版、尽早匹配可用交换论证证明最优、时间 O(|s|) 空间 O(1)。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def appendCharacters(self, s: str, t: str) -> int: n, j = len(t), 0 for c in s: if j < n and c == t[j]: j += 1 return n - j复杂度
- 时间:O(|s|),只把 s 从头到尾扫一遍,每个字符做一次常数比较;j 最多前进 |t| 次,不会让复杂度升级。整体随 s 的长度线性增长,记 O(|s|);t 没有再扫一遍,只取了它的长度算差值
- 空间:O(1),只用了指针 j 和长度 n 这几个变量,不额外开数组或哈希,空间是常数
易错点
面试追问把动画讲成自己的话
追问这题和判断 t 是不是 s 的子序列是一回事吗?
追问贪心让每个字符尽早匹配,为什么一定最优?
追问复杂度是多少?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
统计完全子数组的数目
LeetCode 2799 · 中等 · 沿着 双指针套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题