LeetCode 522中等双指针 · 滑窗
最长特殊序列 II 图解题解
这道题到底在问什么
给字符串列表 strs,返回最长「特殊序列」的长度。特殊序列 = 某串独有的子序列(不是其它任何串的子序列)。子序列可由删去原串中若干字符得到,不要求连续。都不满足则返回 -1。
- 输入
- strs=["aba","cdc","eae"]
- 输出
- 3 (三个串互不为对方子序列,最长 3)
- 输入
- strs=["aaa","aaa","aa"]
- 输出
- -1 (aaa 互为子序列,aa 也被包含)
最优解:一步一步想明白
- 3记住「逐个串去比,被别人包含就淘汰,扛过所有人就特殊」,下面每一帧都在套它。
- 4轮到第 0 个串 "aba" 当候选。逐个拿别的串去试:只要它是某个串的子序列就被淘汰,全扛住才算特殊。
- 5t 的第 0 位 "c" 不是候选要找的 "a",i 指针不动,j 继续右移找下一个。
- 6t 的第 1 位 "d" 不是候选要找的 "a",i 指针不动,j 继续右移找下一个。
- 7t 的第 2 位 "c" 不是候选要找的 "a",i 指针不动,j 继续右移找下一个。
- 8整个 "cdc" 扫完了,"aba" 没能被它完整包含,"aba" 不是 "cdc" 的子序列。继续拿下一个串来比。
- 9t 的第 0 位 "a" 正好等于候选要找的 "a",命中!候选已匹配 1/3 个字符。
- 10t 的第 1 位 "b" 正好等于候选要找的 "b",命中!候选已匹配 2/3 个字符。
- 11t 的第 2 位 "a" 正好等于候选要找的 "a",命中!候选已匹配 3/3 个字符。"aba" 被 "aba" 完整包含了。
- 12候选 "aba" 被 "aba" 完整包含,它不是独有的,整串标红淘汰。
- 13轮到第 1 个串 "cdc" 当候选。逐个拿别的串去试:只要它是某个串的子序列就被淘汰,全扛住才算特殊。
- 14t 的第 0 位 "a" 不是候选要找的 "c",i 指针不动,j 继续右移找下一个。
- 15t 的第 1 位 "b" 不是候选要找的 "c",i 指针不动,j 继续右移找下一个。
- 16t 的第 2 位 "a" 不是候选要找的 "c",i 指针不动,j 继续右移找下一个。
- 17整个 "aba" 扫完了,"cdc" 没能被它完整包含,"cdc" 不是 "aba" 的子序列。继续拿下一个串来比。
- 18又一个 "aba",和刚才比过的那个完全相同,"cdc" 的第一个字符 "c" 在里面照样找不到,直接跳过。
- 19t 的第 0 位 "a" 不是候选要找的 "c",i 指针不动,j 继续右移找下一个。
- 20t 的第 1 位 "b" 不是候选要找的 "c",i 指针不动,j 继续右移找下一个。
- 21整个 "ab" 扫完了,"cdc" 没被它完整包含。所有对手都比完了,没人包含 "cdc",准备判它是特殊序列。
- 22候选 "cdc" 扛过了所有对手,谁也包不住它,它就是特殊序列!整串标绿,用它的长度 3 刷新答案为 3。
- 23轮到第 2 个串 "aba" 当候选。逐个拿别的串去试:只要它是某个串的子序列就被淘汰,全扛住才算特殊。
- 24t 的第 0 位 "a" 正好等于候选要找的 "a",命中!候选已匹配 1/3 个字符。
- 25t 的第 1 位 "b" 正好等于候选要找的 "b",命中!候选已匹配 2/3 个字符。
- 26t 的第 2 位 "a" 正好等于候选要找的 "a",命中!候选已匹配 3/3 个字符。"aba" 被 "aba" 完整包含了。
- 27候选 "aba" 被 "aba" 完整包含,它不是独有的,整串标红淘汰。
- 28轮到第 3 个串 "ab" 当候选。逐个拿别的串去试:只要它是某个串的子序列就被淘汰,全扛住才算特殊。
- 29t 的第 0 位 "a" 正好等于候选要找的 "a",命中!候选已匹配 1/2 个字符。
- 30t 的第 1 位 "b" 正好等于候选要找的 "b",命中!候选已匹配 2/2 个字符。"ab" 被 "aba" 完整包含了。
- 31候选 "ab" 被 "aba" 完整包含,它不是独有的,整串标红淘汰。
- 32回看全程:两个 "aba" 互为子序列、"ab" 是 "aba" 的子序列,全被淘汰;只有 "cdc" 谁也包不住,它就是最长特殊序列,答案 3。
⚠️ 容易写错的地方
✗ 错:直接取最长的串当答案
✓ 对:最长串若和别的串相等或被包含也会被淘汰
两个相同的最长串互为子序列,谁都不特殊
✗ 错:比较时漏了跳过自己
✓ 对:内层 j 必须 i ≠ j
任何串都是自己的子序列,不跳过会把所有串都误判成非特殊
✗ 错:把「子序列」当成「子串」
✓ 对:子序列可跳着删字符,不要求连续
aa 是 aba 的子序列(取第 0、2 位的 a),但不是连续子串
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def findLUSlength(self, strs: List[str]) -> int:
def check(s: str, t: str):
i = j = 0
while i < len(s) and j < len(t):
if s[i] == t[j]:
i += 1
j += 1
return i == len(s)
ans = -1
for i, s in enumerate(strs):
for j, t in enumerate(strs):
if i != j and check(s, t):
break
else:
ans = max(ans, len(s))
return ansC++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
int findLUSlength(vector<string>& strs) {
int ans = -1;
int n = strs.size();
auto check = [&](const string& s, const string& t) {
int m = s.size(), n = t.size();
int i = 0;
for (int j = 0; i < m && j < n; ++j) {
if (s[i] == t[j]) {
++i;
}
}
return i == m;
};
for (int i = 0, j; i < n; ++i) {
int x = strs[i].size();
for (j = 0; j < n; ++j) {
if (i != j && check(strs[i], strs[j])) {
x = -1;
break;
}
}
ans = max(ans, x);
}
return ans;
}
};Java
import java.util.*;
class Solution {
public int findLUSlength(String[] strs) {
int ans = -1;
int n = strs.length;
for (int i = 0, j; i < n; ++i) {
int x = strs[i].length();
for (j = 0; j < n; ++j) {
if (i != j && check(strs[i], strs[j])) {
x = -1;
break;
}
}
ans = Math.max(ans, x);
}
return ans;
}
private boolean check(String s, String t) {
int m = s.length(), n = t.length();
int i = 0;
for (int j = 0; i < m && j < n; ++j) {
if (s.charAt(i) == t.charAt(j)) {
++i;
}
}
return i == m;
}
}复杂度
时间
O(n² · L)
n 个串两两比较,每次子序列判定 O(L)
空间
O(1)
只用双指针 i、j 和记分变量
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 最长特殊序列 II 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这题和「最长特殊序列 I」(LC521)有什么区别?+
LC521 只有两个串,答案直接是较长串的长度(若两串不等),因为更长的串绝不可能是更短串的子序列,相等才返回 -1。LC522 有多个串、还可能重复或互相包含,必须把每个串都拿去逐个双指针判子序列,不能只看长度。
能不能先排序来优化?+
可以按长度从大到小排,第一个不被任何更长或等长串包含的串就是答案,能提前结束。但最坏情况仍是每对都要比、每比一次还要 O(L) 判定,总复杂度量级不变,还是 O(n²·L)。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 最长特殊序列 II 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。