给定文本txt [0..n-1]和模式pat [0..m-1],编写一个函数search(char pat [],char txt []),将所有出现的pat []都打印在txt中[]。您可以假设n> m。
预处理模式还是预处理文本?
在之前的文章中,我们讨论了以下算法:
KMP算法
Rabin Karp算法
基于有限自动机的算法
博耶摩尔算法
以上所有算法都对模式进行了预处理,以使模式搜索更快。我们可以通过预处理模式获得的最佳时间复杂度是O(n),其中n是文本的长度。在本文中,我们将讨论一种预处理文本的方法。后缀树是由文本构成的。在预处理文本(构建文本的后缀树)之后,我们可以搜索O(m)时间中的任何模式,其中m是模式的长度。
想象一下,您已经存储了威廉·莎士比亚的全部作品并对其进行了预处理。您可以在整个工作中按与模式长度成正比的时间搜索任何字符串。这确实是一个很大的改进,因为模式的长度通常比文本小得多。
如果文本经常更改,则预处理可能会变得很昂贵。不过,这对于固定文本或不常更改的文本很有用。
给定文本的后缀树是给定文本所有后缀的压缩特里。我们已经讨论了标准Trie。让我们用下面的单词数组来理解压缩特里。
{bear, bell, bid, bull, buy, sell, stock, stop}
以下是上述输入单词集的标准特里。
以下是压缩的特里。通过连接单个节点的链从标准Trie中获得Compress Trie。可以通过在节点上存储索引范围来存储压缩特里树的节点。
如何为给定的文本构建后缀树?
如上所述,后缀树是所有后缀的压缩特里,因此下面是从给定文本构建后缀树的非常抽象的步骤。
1)生成给定文本的所有后缀。
2)将所有后缀视为单个单词,并构建压缩的特里。
让我们考虑示例文本“ banana \ 0”,其中“ \ 0”是字符串终止字符。以下是“ banana \ 0”的所有后缀
banana\0
anana\0
nana\0
ana\0
na\0
a\0
\0
如果我们将上述所有后缀视为单个单词并构建一个词条,则会得到关注。
如果我们连接单个节点的链,则会得到以下压缩的特里树,即给定文本“ banana \ 0”的后缀树
请注意,以上步骤仅用于手动创建后缀树。我们将在另一篇文章中讨论实际的算法和实现。
如何在构建的后缀树中搜索模式?
上面我们讨论了如何构建后缀树,这是模式搜索中的预处理步骤。以下是在构建的后缀树中搜索模式的抽象步骤。
1)从模式的第一个字符和后缀树的根开始,对每个字符进行以下操作。
….. a)对于样式的当前字符,如果后缀树的当前节点有一条边,则跟随该边。
….. b)如果没有边缘,则打印“文本中不存在图案”并返回。
2)如果图案的所有字符均已处理,即从根到指定图案的字符有路径,则打印“找到图案”。
让我们将示例模式视为“ nan”以查看搜索过程。下图显示了搜索“ nan”或“ nana”所遵循的路径。
这是如何运作的?
文本中出现的每个模式(或者我们可以说文本的每个子字符串)都必须是所有可能后缀之一的前缀。该语句似乎很复杂,但是它是一个简单的语句,我们只需要举一个例子来检查它的有效性。
后缀树的应用
后缀树可用于多种问题。以下是后缀树提供最佳时间复杂度解决方案的一些著名问题。
1)模式搜索
2)找到最长的重复子串
3)找到最长的公共子串
4)查找字符串最长的回文
还有更多的应用程序。有关更多详细信息,请参见此内容。
在以下文章中讨论了Ukkonen的后缀树构造:
Ukkonen的后缀树构造–第1部分
Ukkonen的后缀树构造–第2部分
Ukkonen的后缀树构造–第3部分
Ukkonen的后缀树构造–第4部分
Ukkonen的后缀树构造–第5部分
Ukkonen的后缀树构造–第6部分