查询字符串S是否包含子串S1
从Trie树(字典树)谈到后缀树
作者:July、yansha。
出处:http://blog.csdn.net/v_JULY_v 。
引言
常关注本blog的读者朋友想必看过此篇文章:从B树、B+树、B*树谈到R 树,这次,咱们来讲另外两种树:Tire树与后缀树。不过,在此之前,先来看两个问题。
第一个问题: 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。
之前在此文:海量数据处理面试题集锦与Bit-map详解中给出的参考答案:用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平均长度),然后是找出出现最频繁的前10个词。也可以用堆来实现(具体的操作可参考第三章、寻找最小的k个数),时间复杂度是O(n*lg10)。所以总的时间复杂度,是O(n*le)与O(n*lg10)中较大的哪一个。
第二个问题:找出给定字符串里的最长回文。例子:输入XMADAMYX。则输出MADAM。这道题的流行解法是用后缀树(Suffix Tree),但其用途远不止如此,它能高效解决一大票复杂的字符串编程问题(当然,它有它的弱点,如算法实现复杂以及空间开销大),概括如下:
- 查询字符串S是否包含子串S1。主要思想是:如果S包含S1,那么S1必定是S的某个后缀的前缀;又因为S的后缀树包含了所有的后缀,所以只需对S的后缀树使用和Trie相同的查找方法查找S1即可(使用后缀树实现的复杂度同流行的KMP算法的复杂度相当)。
- 找出字符串S的最长重复子串S1。比如abcdabcefda里abc同da都重复出现,而最长重复子串是abc。
- 找出字符串S1同 S2的最长公共子串。注意最长公共子串(Longest CommonSubstring)和最长公共子序列(LongestCommon Subsequence, LCS)的区别:子串(Substring)是串的一个连续的部分,子序列(Subsequence)则是从不改变序列的顺序,而从序列中去掉任意的元素 而获得的新序列;更简略地说,前者(子串)的字符的位置必须连续,后者(子序列LCS)则不必。比如字符串acdfg同akdfc的最长公共子串为df, 而他们的最长公共子序列是adf。LCS可以使用动态规划法解决。
- Ziv-Lampel无损压缩算法。 LZW算法的基本原理是利用编码数据本身存在字符串重复特性来实现数据压缩,所以一个很好的选择是使用后缀树的形式来组织存储字符串及其对应压缩码值的字典。
- 找出字符串S的最长回文子串S1。例如:XMADAMYX的最长回文子串是MADAM(此即为上面所说的第二个问题:最长回文问题,本文第二部分将详细阐述此问题)。
- 多模式串的模式匹配问题(suffix_array + 二分)。
本文第一部分,咱们就来了解这个Trie树,然后自然而然过渡到第二部分、后缀树,接着进入第三部分、详细阐述后缀树的构造方法-Ukkonen,最后第四部分、对自动机,KMP算法,Extend-KMP,后缀树,后缀数组,trie树,trie图及其应用做个全文概括性总结。权作此番阐述,以备不时之需,在需要的时候便可手到擒来。ok,有任何问题,欢迎不吝指正或赐教。谢谢。