一键导入
algo-string-algos
String algorithms cheat-sheet — KMP, Rabin-Karp rolling hash, Z-array, suffix arrays, anagram patterns, and when to use built-ins.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
String algorithms cheat-sheet — KMP, Rabin-Karp rolling hash, Z-array, suffix arrays, anagram patterns, and when to use built-ins.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Breadth-first search — shortest path on unweighted graphs, level-order traversal, and the `visited` discipline that prevents O(2^n) blowups.
Binary search invariants, half-open intervals, and the `lo<hi` template that beats off-by-one bugs.
Depth-first search — recursive vs iterative, recursion-depth gotchas, three-color cycle detection, topological sort.
Dynamic programming — state design, memoization vs tabulation, dimension-reduction, and when DP is the wrong tool.
Pick the right traversal — BFS for unweighted shortest path, Dijkstra for weighted, A* for goal-directed, 0/1-BFS for binary weights.
Greedy algorithms — exchange-argument proofs, when greedy beats DP, classic patterns (interval scheduling, Huffman, scheduling).
| name | algo-string-algos |
| description | String algorithms cheat-sheet — KMP, Rabin-Karp rolling hash, Z-array, suffix arrays, anagram patterns, and when to use built-ins. |
| when-to-use | Substring search, multi-pattern match, longest palindrome / repeated substring, anagram grouping, prefix-function applications. |
Substring search has three algorithms worth knowing: built-in find (don't reinvent), KMP (deterministic), and rolling hash (handles multi-pattern). Beyond that, the Z-array unlocks "all matches" and "longest common prefix"; suffix arrays unlock "longest repeated substring".
| problem | algorithm | complexity |
|---|---|---|
| single pattern, single text | built-in find | engine-defined |
| single pattern, billions chars | KMP | O(n + m) |
| many patterns at once | Aho-Corasick | O(n + m + z) |
| dynamic / 2D match | rolling hash | O(n + m) exp. |
| longest palindrome | Manacher | O(n) |
| anagram groups | sorted-key bucket | O(n k log k) |
| longest repeated substring | suffix array + LCP | O(n log n) |
pi[i] is the length of the longest
proper prefix of pattern[:i+1] that is also a suffix. Built
in O(m).H(s) = sum(s[i] * b^(n-1-i)) mod p. Update is
H' = (H * b + s[n]) mod p (extend) or (H - s[0] * b^(n-1)) * b + s[n+1] mod p (slide).(base, prime) pairs make
false positives effectively impossible (probability < 1/p²).def kmp_search(text: str, pattern: str) -> list[int]:
"""All start indices where pattern occurs in text."""
if not pattern:
return list(range(len(text) + 1))
pi = _failure(pattern)
out = []
j = 0
for i, ch in enumerate(text):
while j and ch != pattern[j]:
j = pi[j - 1]
if ch == pattern[j]:
j += 1
if j == len(pattern):
out.append(i - j + 1)
j = pi[j - 1]
return out
def _failure(p: str) -> list[int]:
pi = [0] * len(p)
k = 0
for i in range(1, len(p)):
while k and p[i] != p[k]:
k = pi[k - 1]
if p[i] == p[k]:
k += 1
pi[i] = k
return pi
function rabinKarp(text, pattern) {
if (pattern.length > text.length) return [];
const B1 = 131n, M1 = 1_000_000_007n;
const B2 = 137n, M2 = 998_244_353n;
const m = pattern.length;
let pH1 = 0n, pH2 = 0n, tH1 = 0n, tH2 = 0n;
let pow1 = 1n, pow2 = 1n;
for (let i = 0; i < m; i++) {
pH1 = (pH1 * B1 + BigInt(pattern.charCodeAt(i))) % M1;
pH2 = (pH2 * B2 + BigInt(pattern.charCodeAt(i))) % M2;
tH1 = (tH1 * B1 + BigInt(text.charCodeAt(i))) % M1;
tH2 = (tH2 * B2 + BigInt(text.charCodeAt(i))) % M2;
if (i < m - 1) { pow1 = pow1 * B1 % M1; pow2 = pow2 * B2 % M2; }
}
const out = [];
for (let i = 0; i <= text.length - m; i++) {
if (tH1 === pH1 && tH2 === pH2) out.push(i);
if (i + m < text.length) {
const drop1 = BigInt(text.charCodeAt(i)) * pow1 % M1;
const drop2 = BigInt(text.charCodeAt(i)) * pow2 % M2;
tH1 = ((tH1 - drop1 + M1) * B1 + BigInt(text.charCodeAt(i + m))) % M1;
tH2 = ((tH2 - drop2 + M2) * B2 + BigInt(text.charCodeAt(i + m))) % M2;
}
}
return out;
}
str.find would do. Built-in is
faster on most real inputs; KMP wins only when the pattern has
long self-overlap.pi[i] is the length of
the longest proper prefix-suffix of p[:i+1] — proper meaning
< i + 1.O(k log k); counter
is O(k). For long strings, counter wins.text.codePointAt)
or accept O(n) reindexing cost."aaaa") and text of the
same ("aaaaaaa") — exercises overlapping matches."abab" in "ababab") — exercises the failure jump.from collections import defaultdict
def group_anagrams(words: list[str]) -> list[list[str]]:
groups: dict[str, list[str]] = defaultdict(list)
for w in words:
key = "".join(sorted(w))
groups[key].append(w)
return list(groups.values())
For very long words, swap the sorted-key for a 26-int tuple of character counts to get linear-per-word grouping.
find / indexOf.