应用
串的模式匹配:在主串中确定模式串的位置
主串S:S[0]、S[1]、……、S[n-1],长度为n
模式串P:P[0]、P[1]、……、P[m-1],长度为m
next数组:根据模式串的性质生成的,next[i]=k 表示:当模式串中位置i处的字符不匹配时,需要跳转至位置k处进行比较
如何确定next数组?
若next[i] = k,则有:P[0]P[1]……P[k-1] = P[i-k]P[i-k+1]……P[i-1]
我们从前向后推,next[0] = -1,表示当与模式串的位置0处的字符不匹配时,需要将主串向后移动1位,
对于已经确定的 next[i] = k ,推导next[i+1]
(1) 若P[k] == P[i],则有:P[0]P[1]……P[k-1]P[k] = P[i-k]P[i-k+1]……P[i-1]P[i],得next[i+1]=k+1
(2)若P[k] != P[i],即P[0]P[1]……P[k-1]P[k] != P[i-k]P[i-k+1]……P[i-1]P[i],可以看成前缀P[0]P[1]……P[k-1]P[k]在后缀P[i-k]P[i-k+1]……P[i-1]P[i]上匹配,当P[k]与P[i]不匹配时,k则应回溯至next[k]继续进行匹配
为什么是k=next[k]?
假设next[k]=c,则有:P[0]P[1]……P[c-1] = P[k-c]P[k-c+1]……P[k-1]
因为next[i]=k,有:P[0]P[1]……P[k-1] = P[i-k]P[i-k+1]……P[i-1]
得:P[0]P[1]……P[c-1] = P[i-c]P[i-c+1]……P[i-1]
若P[c] == P[i],则有P[0]P[1]……P[c-1]P[c] = P[i-c]P[i-c+1]……P[i-1]P[i],得next[i+1]=c+1
next数组的优化
考虑模式串P="aaab",可得next数组如下第二行,由实际可知,当P[2]不匹配时,P[1]、P[0]也必定不匹配,也就没有必要再次进行比较,改进方法是判断P[i]与P[next[i]]是否相同,若相同则next[i] = next[next[i]],改进后的next数组如下第三行所示
j | 0 | 1 | 2 | 3 |
next[j] | -1 | 0 | 1 | 2 |
next[j]' | -1 | -1 | -1 | 2 |
进行匹配
主串一直向前走,模式串根据next数组确定不匹配时的位置,适当后退。
当模式串的位置为-1时表示主串与模式串将要进行新的匹配,主串位置+1,模式串从头开始进行匹配
C++实现
#include <iostream>
#include <string>
using namespace std;
void getNext(string pattern, int* next){
int length = pattern.length();
int i=0,j=-1;
next[0]=-1;
while (i<length-1){
if( -1==j || pattern[j] == pattern[i] ){
++i;++j;
if(pattern[i] != pattern[j])
next[i]=j;
else
next[i]=next[j];
}else
j=next[j];
}
}
int KMP(string s,string pattern,int* next){
int sLength = s.length();
int pLength = pattern.length();
int sIndex=0,pIndex=0;
while ( sIndex<sLength && pIndex<pLength ){
if( -1==pIndex || s[sIndex] == pattern[pIndex]){
++sIndex;++pIndex;
}else
pIndex = next[pIndex];
}
if(pIndex>=pLength)
return sIndex-pIndex;
return -1;
}