C小加

厚德 博学 求真 至善 The bright moon and breeze
posts - 145, comments - 195, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

KMP算法中关于next数组的探究

Posted on 2011-09-20 18:11 C小加 阅读(5037) 评论(8)  编辑 收藏 引用 所属分类: 数据结构和算法

从《严书》上看到了KMP算法,看了一遍没懂,但觉得挺神奇的,就花费了几天时间深入的理解。

算法的原理其实不难,难的就是那个巧妙的next数组,这个next数组很吸引我,我的大部分时间也都是花费在这个数组上面的。这个next数组是KMP里面一个很关键的地方,对于在数据结构书上看过一遍整个算法流程的人,能够把next数组搞明白,整个KMP算法的整体思想就差不多理解了。然后在一些细节上面深入思考一下,就可以理解和领会改进的KMP算法。

 

一、KMP算法简单介绍

KMP算法是字符串匹配算法的一种,相对于朴素的字符串匹配算法而言,可以大大避免重复遍历的情况。此算法可以在On+m)的时间数量级上完成字符串匹配操作。

二、神奇的next数组

关于KMP算法的原理和实现,书上或者百度一下都可以找到,我在这里就不罗嗦那么多了,直接切入主题(next数组)。

我们设主串S=abcabcabca,模式串p=abcabx

KMP第一趟匹配:

                         i=6                    

S    :   a  b  c  a  b  c  a   b  c  a

位置 :  1  2  3  4  5  6  7  8  9  10

P    :   a  b   c  a  b  x

位置 :  1  2  3  4  5  6

                           j=6                      

第一次匹配到第6个位置的时候失败了,按照朴素的算法,i要回溯到第2个位置,j要回溯到第1个位置重新匹配。KMP的话,主串中的i是不会回溯,模式串中的j回溯也不会回溯到第1个位置。注意这里是关键,i不用回溯就可以完成整个字符串的匹配。为什么i不需要回溯呢?我们先留下这个疑问。

我们把匹配成功的前5个字符研究一下。

1位置的前缀子串为:a , ab , abc , abca

5位置的后缀子串为:bcab , cab , ab , b

我们观察发现两组里面都有一个ab,你能看出点什么东西么,好的,先不管这个。

我们就按照朴素的算法来看,i回溯到第23位置都会在前5个字符中匹配失败。

朴素匹配:

                   i=4                    

S    :  a  b  c  a  b  c  a   b  c  a

位置 : 1  2  3  4  5  6  7  8  9  10

P    :             a  b  c  a  b  x

位置 :            1  2  3  4  5  6

                   j=1 

当回溯到第4个位置的时候,成功匹配的字符为ab然后再去判断S串的第6个字符和P串的第3个位置。这个然后我们先不管,观察S中和P匹配的ab,在第一趟匹配的时候S中的ab是和P中前5个字符的最后两个匹配的,而这一次匹配则是和P中前两个字符匹配的。能发现点什么东西么?

不需要让i回溯到之前的位置重新匹配,只需要找到在P串前5个字符中第一个位置的前缀子串和最后一个位置的后缀子串相等并且串长最大的那一对子串,让j指向前缀子串最后一个字符的下一个位置3,和i所指向的6进行比较。往后遇见不匹配的时候采取和这个一样的方法。

KMP第二趟匹配:

                           i=6                    

S    :   a  b  c  a  b  c  a   b  c  a

位置 :  1  2  3  4  5  6  7  8  9  10

P    :              a  b  c  a  b  x

位置 :             1  2  3  4  5  6

                           j=3 

这个时候就需要next数组的建立了,next[6]存储的就是前5个字符组成的字符串中的第一个位置的前缀子串和最后一个位置的后缀子串相等并且串长最大的那一对子串的最后一个字符的下一个位置,也就是3,也就是和P串中第3个位置匹配。

写到这里,next数组应该可以得出来了。

具体代码怎么得出来的,书上面都有。。那个应该不难。

对于next数组还有一个优化,《严书》上讲的很清晰。

三、next数组在ACM中的应用

直接用KMP算法真的去匹配两个字符串其实很少见,除非字符串里的字符集范围很小,或字符重复数量过多,用KMP可大减少时间,否则一般都是直接朴素匹配。
kmp
算法在ACM中并不大可能用来直接用,主要有用的是对它的理解和它的精华部分---- next数组,这个的一个用途就是确定重复子串,具体参见 poj2406,poj1961,poj2752。

next数组模板

 

Feedback

# re: KMP算法中关于next数组的探究  回复  更多评论   

2011-09-21 12:38 by ooseven
正则表达式算法现在已经很成熟了,并且DFA方式的正则引擎同样不需要回溯,而且通用性高很多,个人认为没有必要纠结与kmp,实际应用中根本不需要,用dfa正则引擎好用得多,效率也不差。可能就是内存占用稍微高了点。当然,现在很多人研究kmp只是因为考试需要而已。

# re: KMP算法中关于next数组的探究  回复  更多评论   

2011-09-22 09:00 by C小加
@ooseven
我研究kmp是因为兴趣,算法的用处虽然不大,但是思想却是很吸引人的。正则表达式算法我还没接触过,不过看起来也蛮吸引人的。

# re: KMP算法中关于next数组的探究  回复  更多评论   

2011-09-22 11:35 by ooseven
@C小加
正则表达式算法看似简单,但是,动手实践之后你就会发现,要彻底研究透根本就是个无底洞,复杂度主要与状态数的优化。因此,我的策略是能手工写出一个正则引擎就可以了,适可而止!

# re: KMP算法中关于next数组的探究  回复  更多评论   

2011-09-22 16:47 by C小加
@ooseven
这样啊,我想研究一下,不知道怎么才能和大牛进一步交流呢?如果有问题的话想请教一下。

# re: KMP算法中关于next数组的探究  回复  更多评论   

2011-09-22 17:32 by ooseven
@C小加
哈,大牛可不敢当,正则表达式算法属于编译原理里的一门功课,而编译原理在cppblog里唯一的大牛是 陈梓瀚(vczh),我的那个正则引擎在实现的时候还请教过他呢。

# re: KMP算法中关于next数组的探究  回复  更多评论   

2011-09-22 17:49 by C小加
@ooseven
恩恩。看到了。那两篇文章。还有你的评论。嘻嘻。。那位大牛应该正在写编译器吧,而且感觉他对图形图像也很有研究,厉害呀。先拜读那两篇文章吧。

# re: KMP算法中关于next数组的探究  回复  更多评论   

2011-11-03 21:46 by 浅笑
球球。。。

# re: KMP算法中关于next数组的探究  回复  更多评论   

2013-10-12 11:00 by 周炎婷
问一下,神马叫第一个位置的前缀子串?

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理