给定一个文本串s,和一个匹配串p,要求查找p第一次在s中出现的位置。
常见的方法如暴力搜素,逐个匹配s[i],p[j],若匹配,下标后移。不匹配,i回溯到这次匹配前的下一位置,j置为0,重新匹配。最坏情况,时间复杂度O(n*m)。
int violenceSearch(char *s,char *p){
int i=0,j=0;
int lenp=strlen(p);
int lenS=strlen(s);
while(j<lenp&&i<lens){
if(s[i]==p[j]){
i++;
j++;
}
else{
i=i-j+1;
j=0;
}
}
if(j==len) return i-j;
return ;
}
每次s[i] , p[j]不匹配总要将i回溯到到下一位置,j置0,这样做可能还是会导致不匹配,又要i回溯,j置0……造成不必要的开销,kmp查找算法的做法就是令i不回溯,当不匹配的时候令j置为next[j], 再将s[i]与p[next[j]]进行匹配,这种做法的好处是不用回溯i,最坏情况时间复杂度O(n+m)。
int kmpSearch(char *s,char *p){
int i=0,j=0;
int lens=strlen(s);
int lenp=strlen(p);
while(i<lens&&j<lenp){
if(j==-1||s[i]==p[j]){//当j==-1时,表示将匹配串的首位和文本串进行匹配,即i=i+1,j=0.
i++;
j++;
}
else
j=next[j];
}
if(j==lenp)return i-j;
return ;
}
这里维护的next[j]是在匹配串p上p[j]之前(不包含p[j])的子串的相同最长前缀后缀的长度
如给定匹配串ABCDABD,对应next[]:
kmp的精髓应该就在求解next[]数组上,即知道next[0….j],如何求解next[j+1]:
•若p[k] == p[j],则next[j + 1 ] = next [j] + 1 = k + 1;
•若p[k ] ≠ p[j],如果此时p[ next[k] ] == p[j ],则next[ j + 1 ] = next[k] + 1,否则继续递归前缀索引k = next[k],而后重复此过程。 相当于在字符p[j+1]之前不存在长度为k+1的前缀”p0 p1, …, pk-1 pk”跟后缀“pj-k pj-k+1, …, pj-1 pj”相等,那么是否可能存在另一个值t+1 < k+1,使得长度更小的前缀 “p0 p1, …, pt-1 pt” 等于长度更小的后缀 “pj-t pj-t+1, …, pj-1 pj” 呢?如果存在,那么这个t+1 便是next[ j+1]的值,此相当于利用已经求得的next 数组(next [0, …, k, …, j])进行P串前缀跟P串后缀的匹配。
void getnext(char *p,int next[]){
int k=-1;
int j=0;
int next[0]=-1;
int len=strlen(p);
while(j<len-1){
if(k=-1||p[k]==p[j]){//p[k]表示前缀,p[j]表示后缀。
//k=-1时,这时之前的串没有相等的前缀后缀,即next[j+1]=0;
++j;
++k;
next[j]=k;
}
else
k=next[k];
}
}
其实next [j]数组只要将各个最大前缀后缀(这时包括next[j]这个元素)的公共元素的长度值右移一位,且把初值赋为-1 即可。
next数组的优化:
当匹配串p[j]!=s[i]的时候,这时就要用p[next[j]]与s[i]进行匹配但是如
果p[j]==p[next[j]],就一定会导致这次的匹配失败。所以不允许p[j]==p
[next[j]],如果相等,就再次递归让p[next[next[j]]]与s[i]进行匹配
(即next[j]=next[next[j]])。
void GetNextval(char *p,int next[]){
int k=-1;
int j=0;
int next[0]=-1;
int len=strlen(p);
while(j<len-1){
if(k=-1||p[k]==p[j]){ //p[k]表示前缀,p[j]表示后缀。
//k=-1时,这时之前的串没有相等的前缀后缀,即next[j+1]=0;
++j;
++k;
if(p[j]!=p[k])
next[j]=k;//没改动前只有这行赋值。
else
next[j]=next[k];//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k]
}
else
k=next[k];
}
}
唯一参考:http://blog.csdn.net/v_july_v/article/details/7041827
精简了参考的这篇经典博文,是学习的一个记录,很多细节都没详细的阐述。