kmp详细的原理:
上面这个讲得很详细,很容易听懂;
对于给定的模式串:ABCDABD,它的最大长度表及next 数组分别如下:
下面是根据上面那个视频讲解写的代码,用的是最大长度值
int next[10000];
void Getnext(char * p)
{
int i=1,j=0;
next[0]=0;
int len=strlen(p);
while(i<len){
if(p[i]==p[j]){
next[i]=j+1;
i++;
j++;
}
else{
if(j==0){
next[i]=0;
i++;
}
else{
j=next[j-1];
}
}
}
}
int KmpSearch(char* s, char* p)
{
int i = 0;
int j = 0;
int sLen = strlen(s);
int pLen = strlen(p);
while (i < sLen && j < pLen) {
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if (j == 0 || s[i] == p[j]) {
i++;
j++;
} else {
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = next[j-1];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
下面用的是next数组
void GetNext(char* p,int next[])
{
int pLen = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k])
{
++k;
++j;
next[j] = k;
}
else
{
k = next[k];
}
}
}
int KmpSearch(char* s, char* p)
{
int i = 0;
int j = 0;
int sLen = strlen(s);
int pLen = strlen(p);
while (i < sLen && j < pLen) {
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if (j == -1 || s[i] == p[j]) {
i++;
j++;
} else {
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = next[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
下面讲一下kmp的扩展应用:求周期串的周期(求最小重复字串的个数)
比如:
如 abababab由4个ab组成
分析:
kmp中的next数组求最小循环节的应用
例如
ababab next[6] = 4; 即
ababab
ababab
1~4位 与2~6位是相同的
那么前两位
就等于3、4位
3、4位就等于5、6位
……
所以 如果 能整除 也就循环到最后了
如果不能整除
就最后余下的几位不在循环内
例如
1212121
1212121
最后剩余1不能等于循环节
#include<stdio.h>
#include<string.h>
#define max 1000000
int next[max];
char str1[max];
int get_next(char *pat)
{
int j=0,k=-1;
int len=strlen(pat);
next[0]=-1;
while(j<len)
{
if(k==-1||pat[j]==pat[k])
next[++j]=++k;
else
k=next[k];
}
j=len-k;//如果最后一个位置不匹配,那么就会滚到len-k的位置,也就是最小重复字串的长度。
if(len%j==0)
return len/j;
else
return 1;
}
int main()
{
while(scanf("%s",&str1)!=EOF)
{
if(str1[0]=='.')
break;
printf("%d\n", get_next(str1));
}
return 0;
}