Z算法的实现

3
自从四天前,我一直在阅读关于字符串以及一些算法的内容,用于模式匹配,我找到了KMP搜索算法,这很不错,但是我还发现有另一种与KMP算法在时间和空间复杂度上相同的字符串匹配方法,但它有一个简单的解决方案。
该算法被称为Z算法。
因此,我在谷歌上进行了搜索,但没有找到一个好的算法解释。请问您可以说明如何创建模式数组以及如何应用搜索过程吗?如果您能提供C++代码的话,那就更好了。

1
请阅读此链接:http://codeforces.com/blog/entry/3107 - Bad Coder
我认为你不可能在其他任何地方找到更好的解释。https://www.youtube.com/watch?v=CpZh4eF8QBw - Yogesh Yadav
刚才在另一个问题下写了一篇:https://dev59.com/n5Lea4cB1Zd3GeqP794a#59223889 希望能有所帮助。 - Kun Hu
2个回答

8

经过一段时间的学习,我终于明白了如何构建Z数组。我会在这里用简单的语言解释。

首先让我们了解什么是前缀:

例如:

  1. 在单词apple中,前缀可以是apple(或)appl(或)app(或)ap(或)a。

  2. 在单词banana中,前缀可以是banana(或)banan(或)bana(或)ban(或)ba(或)b。

解释:字符串T的任何子字符串S,如果从字符串T的开头匹配到字符串T的末尾或在末尾之前,则称为前缀。

希望您已经理解了这里的前缀是什么。

现在看看如何构建Z数组。

让我们以这个例子文本为例:a a b $ b a a b a a

索引:0 1 2 3 4 5 6 7 8 9

文本:a a b $ b a a b a a

Z值:x 1 0 0 0 3 1 0 2 1

注意:

a. 子字符串应该从第i个位置开始。

b. 子字符串应该是最长的同时也是一个前缀

  1. 在索引0处:

从i(第0个索引)到末尾找到也是给定文本前缀的子字符串。

a a b $ b a a b a a => 长度为10的最长子字符串,也是文本的前缀。但这不能帮助模式匹配,所以我们将其设置为Z数组中的x。

  1. 在索引1处:

从位置1开始查找直到末尾的最长子字符串,该子字符串也是给定文本的前缀。

这样的子字符串有:

a. "a" => 文本"a a b $ b a a b a a"的前缀,长度为1。

b. "a b" => 不是前缀

c. "a b $" => 不是前缀

d. "a b $ b" => 不是前缀

e. "a b $ b a" => 不是前缀

等等...

这里唯一的最长子字符串也是一个前缀是"a",它的长度为1。它被存储在Z数组中。

  1. 在索引2处:

子字符串如下:

a. "b" => 不是前缀

b. "b $" => 不是前缀

c. "b $ b" => 不是前缀

等等...

这里没有子字符串也是文本T的前缀。因此,我们在Z数组的索引2处存储零。

在索引5处:
子字符串为:
a. "a" => 文本 "a a b $ b a a b a a" 的前缀,长度为1。
b. "a a" => 文本 "a a b $ b a a b a a" 的前缀,长度为2。
c. "a a b" => 文本 "a a b $ b a a b a a" 的前缀,长度为3。
d. "a a b a" => 不是前缀
因此,这里最长的既是子串又是文本T的前缀的子串是长度为3的"a a b"。所以我们在Z数组的索引5处存储3。
最后,如果Z数组中的任何值与模式的长度相同,则该模式存在于文本T中。

我理解了算法的运作方式,但在理解相应代码方面有些困难。我是从G4G学到这个算法的,但代码的else部分没有被充分记录说明...... 你能解释一下吗?这是链接:http://www.geeksforgeeks.org/z-algorithm-linear-time-pattern-searching-algorithm/ - Saras Arya
1
@MurliManohar: 真是非常棒的解释。我花了6个月时间都没能理解,但只用了5分钟就搞明白了。谢谢! - Rasmi Ranjan Nayak

1
在Z-algo中,我们构建一个Z数组。
什么是Z数组? 对于一个字符串str[0..n-1],Z数组的长度与该字符串相同。 Z数组的元素Z[i]存储从str[i]开始的最长子字符串的长度,该子字符串也是str[0..n-1]的前缀。 Z数组的第一个条目没有意义,因为完整的字符串总是它自己的前缀。
> Example: Index            0   1   2   3   4   5   6   7   8   9  10  11
> Text                      a   a   b   c   a   a   b   x   a   a   a   z   
> values         X          1   0   0   3   1   0   0   2   2   1   0  More
> Examples: str  = "aaaaaa" Z[]  = {x, 5, 4, 3, 2, 1}
> 
> str = "aabaacd" Z[] = {x, 1, 0, 2, 1, 0, 0}
> 
> str = "abababab" Z[] = {x, 0, 6, 0, 4, 0, 2, 0}

这个想法是将模式和文本连接起来,创建一个字符串“P $ T”,其中P是模式,$是一个特殊字符,不应该出现在模式和文本中,T是文本。为连接的字符串构建Z数组。在Z数组中,如果任何一点的Z值等于模式长度,则该模式存在于该点。
Example:
Pattern P = "aab",  Text T = "baabaa"

The concatenated string is = "aab$baabaa"

Z array for above concatenated string is {x, 1, 0, 0, 0, 
                                          3, 1, 0, 2, 1}.
Since length of pattern is 3, the value 3 in Z array 
indicates presence of pattern. 

您可以在这里找到详细的解释和实现


是的,我从极客那里学到了这个,但我不明白他们如何在O(n)时间复杂度内创建一个z数组...? - Nikhil Bhawsar
我无法理解整个else部分,即“如果Z [k]小于剩余区间,则Z [i]将等于Z [k]”。 - Saras Arya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接