字符串匹配——KMP算法

对于正常的字符串模式匹配,主串长度为m,子串为n,时间复杂度会到达O(m*n),而若是用KMP算法,复杂度将会减小线型时间O(m+n),这已是很是高效的匹配算法。c++


设主串为ptr="ababaaababaa";要比较的子串为a=“aab”;算法

KMP算法用到了next数组,而后利用next数组的值来提升匹配速度,我首先讲一下next数组怎么求,以后再讲匹配方式。数组

next数组详解ruby

首先是理解KMP算法的第一个难关是next数组每一个值的肯定。微信

定义一串字符串ui

ptr = "ababaaababaa";spa

next[i](i从1开始算)表明着,除去第i个数,在一个字符串里面从第一个数到第(i-1)字符串前缀与后缀最长重复的个数。.net

什么是前缀?3d

在“aba”中,前缀就是“ab”,除去最后一个字符的剩余字符串。code

同理能够理解后缀。除去第一个字符的后面所有的字符串。

 

在“aba”中,前缀是“ab”,后缀是“ba”,那么二者最长的子串就是“a”;

在“ababa”中,前缀是“abab”,后缀是“baba”,两者最长重复子串是“aba”;

在“abcabcdabc”中,前缀是“abcabcdab”,后缀是“bcabcdabc”,两者最长重复的子串是“abc”;

 

这里有一点要注意,前缀必需要从头开始算,后缀要从最后一个数开始算,中间截一段相同字符串是不行的。

 

再回到next[i]的定义,对于字符串ptr = "ababaaababaa";

next[1] = -1,表明着除了第一个元素,以前前缀后缀最长的重复子串,这里是空 ,即"",没有,咱们记为-1,表明空。(0表明1位相同,1表明两位相同,依次累加)。

next[2] = -1,即“a”,没有前缀与后缀,故最长重复的子串是空,值为-1;

next[3] = -1,即“ab”,前缀是“a”,后缀是“b”,最长重复的子串“”;

next[4] = 1,即"aba",前缀是“ab”,后缀是“ba”,最长重复的子串“a”;next数组里面就是最长重复子串字符串的个数

next[5] = 2,即"abab",前缀是“aba”,后缀是“bab”,最长重复的子串“ab”;

next[6] = 3,即"ababa",前缀是“abab”,后缀是“baba”,最长重复的子串“aba”;

next[7] = 1,即"ababaa",前缀是“ababa”,后缀是“babaa”,最长重复的子串“a”;

next[8] = 1,即"ababaaa",前缀是“ababaa”,后缀是“babaaa”,最长重复的子串“a”;

next[9] = 2,即"ababaaab",前缀是“ababaaa”,后缀是“babaaab”,最长重复的子串“ab”;

next[10] = 3,即"ababaaaba",前缀是“ababaaab”,后缀是“babaaaba”,最长重复的子串“aba”;

next[11] = 4,即"ababaaabab",前缀是“ababaaaba”,后缀是“babaaabab”,最长重复的子串“abab”;

next[12] = 5,即"ababaaababa",前缀是“ababaaabab”,后缀是“babaaaababa”,最长重复的子串“ababa”;

 

Next[j]已知 求next[j+1]两步:

1 若串中字符tj =ti ,则next[i+1]=j+1 ,j为当前最长相等先后缀长度(不是全局)

2若tj != ti  将 ti-j+1........ti做为主串,t1......tj做为子串,类比于失配让j=next[j] 继续比较,若知足1则求得next[j+1]。如abcdcd  串中每次前缀都是从a开始的,因此只要每次不断失配后j能跳到a,则代表回跳是对的,后缀同样。

求next数组代码

void Getnex(string m)//对kmp数组的构造{ nex[0]=-1; int k=-1,j=0; while(j<m.size()) { if(k==-1||m[k]==m[j]) { k++;j++; nex[j]=k; }else k=nex[k]; }}


匹配方法

next数组求值 是比较麻烦的,剩下的匹配方式就很简单了。

next数组用于子串身上,根据上面的原理,咱们可以推出子串a=“aab”的next数组的值分别为0,1,2.

 

首先开始计算主串与子串的字符,设置主串用i来表示,子串用j来表示,若是ptr[i]与a[i]相等,那么i与j就都加1:

prt[1]与a[1]相等,i++,j++:

用代码实现就是

 

if( j==0 || ptr[i]==a[j]){ ++i; ++j;}


ptr[2]与a[2]不相等

此时ptr[2]!=a[2],那么令j = next[j],此时j=2,那么next[j] = next[2] = 1.那么此时j就等于1.这一段判断用代码解释的话就是:

 

if( ptr[i]!=a[j]){ j = next[j];}

加上上面的代码进行组合:

在对两个数组进行比对时,各自的i,j取值代码:

 

while( i<ptr.length && j< a.length){ if( j==0 || ptr[i]==a[i] ) { ++i; ++j; next[i] = j; } else { j = next[j]; }}

此时将a[j]置于j此时所处的位置,即a[1]放到j=2处,由于在j=2时出现不匹配的状况。

 

此时再次计算是否匹配,能够看出来a[1]!=ptr[2],那么j = next[j],即此时j = next[1] = 0;

根据上面的代码,当j=0时,执行++i;++j;

此时就变为:

此时ptr[3] = a[1],继续向下走,下一个又不相等了,而后“aab”向后挪一位,这里再也不赘述了,主要的思想已经讲明白了。到最后一直到i = 8,j=3时匹配成功,KMP算法结束。整个过程就结束了。

代码

#include<bits/stdc++.h>bool SUBMIT = false;using namespace std;const int inf = 1000;int nex[inf];string s,h;void Getnex(string m)//对kmp数组的构造{ nex[0]=-1; int k=-1,j=0; while(j<m.size()) { if(k==-1||m[k]==m[j]) { k++;j++; nex[j]=k; }else k=nex[k]; }}int kmp()//用kmp进行匹配{ int k=0,j=0; while(j<h.size()) { if(k==-1||s[k]==h[j]) { k++;j++; }else{ k=nex[k]; cout<<k<<" "<<j<<endl; } if(k == s.size()) return j-k; } return -1;}int main(){ cin>>h>>s; cout<<h<<endl<<s<<endl; Getnex(s); for(int i=0;i<s.size();i++) cout<<nex[i]; cout<<endl; int ans=kmp();    cout<<ans<<endl; return 0;}


本文分享自微信公众号 - WHICH工做室(which_cn)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索