已发布·持续改进
Algorithm
字符串算法用于在文本中查找模式、按前缀检索单词,核心技术包括 KMP、Rabin-Karp、字典树和 Z 算法。
字符串算法研究如何快速、准确地比较和搜索字符序列。最核心的问题是模式匹配,即找出短模式在长文本中出现的所有位置;在词典中查找以某个前缀开头的所有单词、一次查找多个模式也属于这一领域。逐个位置比较的朴素方法在最坏情况下需要与文本长度和模式长度之积成正比的时间。
编辑器查找、日志搜索、搜索框自动补全、入侵检测、文件同步和基因组分析都建立在字符串算法之上。KMP 借助失配表复用已经做过的比较,保证线性时间;Rabin-Karp 用滚动哈希在文本上滑动窗口,省去大部分比较;字典树让单词共享公共前缀,使前缀查询的时间只与单词长度成正比。这一主题在编程面试中也经常出现。
建议先掌握前缀、后缀和边界(border)等术语,并亲手验证朴素匹配为什么慢。然后自己为一个小模式计算前缀函数表,实现 KMP 搜索和字典树,并与标准库的结果对照验证。最后再了解哈希冲突、Unicode 规范化等在实际中会导致结果出错的陷阱。
预先计算模式每个前缀的最长边界长度;失配时不回退文本,而是跳到更短的边界,从而在 O(n + m) 时间内完成搜索。
Rabin-Karp 在 O(1) 时间内更新长度为 m 的窗口哈希并与模式哈希比较,只有哈希相等时才逐字符确认。
沿着字符逐层向下的树,公共前缀只存一次,因此前缀查询和自动补全的时间只与单词长度成正比。
Z 数组在线性时间内求出每个位置开始的后缀与整个字符串的最长公共前缀。处理真实文本时还要注意规范化和索引单位。
prefix_function 计算模式每个前缀的最长边界长度,kmp_search 利用这张表从左到右只读一遍文本,收集所有起始位置。由于每次匹配后把 k 设为 pi[k - 1],像位置 0 和 2 这样重叠的出现也能找到。运行 python string_search.py 即可输出两个结果。
string_search.py
def prefix_function(p: str) -> list[int]:
"""pi[i] = length of the longest proper border of p[:i + 1]."""
pi = [0] * len(p)
k = 0
for i in range(1, len(p)):
while k > 0 and p[i] != p[k]:
k = pi[k - 1]
if p[i] == p[k]:
k += 1
pi[i] = k
return pi
def kmp_search(text: str, pattern: str) -> list[int]:
"""Start positions of every (possibly overlapping) occurrence."""
if not pattern:
return list(range(len(text) + 1))
pi = prefix_function(pattern)
hits, k = [], 0
for i, ch in enumerate(text):
while k > 0 and ch != pattern[k]:
k = pi[k - 1]
if ch == pattern[k]:
k += 1
if k == len(pattern):
hits.append(i - k + 1)
k = pi[k - 1]
return hits
if __name__ == "__main__":
print(prefix_function("ababaca")) # [0, 0, 1, 2, 3, 0, 1]
print(kmp_search("abababcabab", "abab")) # [0, 2, 7]
python string_search.py共六章,带你从安装一步步了解 字符串算法 的核心概念。
在这里提问、分享经验,交流关于 字符串算法 的看法。
还没有讨论。来发起第一个吧。
0 条评论
登录 · 登录后即可发表评论。
来发表第一条评论吧。