“KMP 解决的是单模式精确匹配。字符相等关系必须稳定,文本流中的每个符号一旦处理便不再回看;若问题允许编辑距离、通配符或完整正则语义,状态需要同时表达更多可能位置,不能只把 换成一个宽松谓词…”
形式陈述
Trie(前缀树)把一组词组织为有根树:根表示空串,每条边标记一个字母,同一节点的出边标签彼此不同;从根沿路径读出的串对应前缀,终止标记指出哪些前缀本身是已存单词;根也可被标为终止,以表示空字。即使键集合为空,仍保留这个根节点。插入、查询和删除一个长度
实现字符串键的映射接口时,在每个有终止标记的节点另存一个关联值;根节点也可保存空键的值。插入同一键时覆盖该节点的旧值,查询先检查终止标记,再返回所存值,因此合法值本身不会与“键缺失”混淆。删除键时清除终止标记及其关联值,保留通向其他键的孩子;只回收已无标记且没有孩子的非根节点。枚举时每到一个已标记节点,就输出其路径键与关联值,因而每个现行键值对恰好出现一次。原来的词集合表示相当于所有关联值都取同一个单位值。
直觉
Trie 按字符逐层共享公共前缀,把字符串比较逐字符展开成树导航,避免重复存储相同前缀。从根到某节点的路径标签就是一个前缀,终止标记区分“某词到此结束”和“只是更长词的前缀”。查询时间与键长度成正比而不直接依赖词典大小,代价是节点与稀疏子指针可能占用大量内存。它利用的是字母表结构,不是键的全序比较。
例子与边界
插入 to 先建立 t→o,并把 o 标为词尾;插入 tea 时复用 t,新建 e→a;插入 ten 时继续复用 te,只新建末尾 n。因此三个词只需五个非根节点。查询 te 能到达节点,但它没有词尾标记,所以精确查询失败;前缀查询则从这里枚举得到 tea,ten。输出这些词的时间还要计入遍历和输出字符,不能把整个枚举都记成
图中还存有 in,inn:in 所在节点既是词尾又有孩子,这说明词尾并不等于叶子。删除 tea 时只去掉末尾 a,不能连同仍被 ten 使用的 te 一起删除;删除 in 时则先清除词尾标记,保留通向 inn 的路径。
字符编码粒度必须一致:按 UTF-8 字节建树与按 Unicode 字符建树会有不同深度和边标签。非空键的操作为
推论与应用
词与树定义前缀路径。自动补全、词频和路由最长前缀匹配使用字符 Trie;Aho–Corasick 再加入失败链接实现多模式匹配。二进制 Trie 与 Patricia Trie把整数按 bit 导航,并压缩单分支路径,复杂度通常依赖字长
x-fast/y-fast Trie进一步在 Word-RAM 上组合前缀哈希与分桶,服务整数前驱问题;其期望或最坏界需要明确哈希与字长模型。FM-index则预处理固定文本的 BWT 与 rank,回答模式区间而不保存字典中每个前缀节点。三条路线共享“前缀”图像,却索引不同对象并提供不同接口。
参考资料
- Robert Sedgewick and Kevin Wayne, Algorithms, 4th ed., 2011,§5.2 Tries,字符串符号表、前缀匹配与删除。