KMP算法
目录:
- KMP的由来
- next数组的引入
- 怎样求next数组
- KMP算法的优化
- KMP算法的代码实现
1.KMP算法的由来
如图所示在朴素的模式匹配算法中当某些子串部分匹配时,主串的扫描指针经常性的回溯,而不是直接性的匹配后面的字符,由此引入KMP算法,把这种问题解决。
2.怎么解决上述问题(next数组的引入)
对于上述过程进行总结:
kmp算法
3.next数组的求法
实质:
- 1.将next[1]=0,next[2]=1.
- 2,找到当前前j-1个字符
- 3,分析此子串的前缀和后缀,求出最大相同字符的长度然后+1就是next数组的值
4.KMP算法的优化
虽然模式匹配算法经过优化的效率有所提升但是还是存在一个问题,就是当字符匹配失败时,前面的字符与当前字符相同的时候,这时候采用KMP算法的模式串依旧指针回溯,就是当前的字符已经不匹配了,没有必要再拿与当前字符相同的字符与对应不匹配的字符进行无意义的比较了,这导致了不必要的开销,因此对KMP算法进行了进一步的优化,将next数组进行优化,让对应next数组内的值“简化”
5.KMP的代码实现
//next数组的算法
//next数组:当模式匹配串失配时,next数组对应的元素指导应该用T串的哪个元素进行下一轮的匹配
void get_next(String T,int *next/*int next[]*/)
{
int j=0;//前缀
int i=1;//后缀
next[1]=0;
while(i<T.length/*T[0]*/)
{
if(j==0||T[i]==T[j])
{
++i;
++j;
next[i]=j;
}
else
j=next[j];//回溯
}
}
//完整版代码
#include<stdio.h>
#include<stdlib.h>
typedef char* String;
void get_next(String T,int *next)
{
int j=0;
int i=1;
next[1]=0;
while(i<T[0])
{
if(j==0||T[i]==T[j])
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}
}
int main()
{
char str[255]=" ababaaaba";
int next[255];
str[0]=9;
get_next(str,next);
for(int i=1;i<10;i++)
{
printf("%d",next[i]);
}
}
//1.普通的KMP
#include<stdio.h>
#include<stdlib.h>
typedef char* String;
void get_next(String T,int *next)
{
int j=0;
int i=1;
next[1]=0;
while(i<T[0])
{
if(j==0||T[i]==T[j])
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}
}
//若存在,则返回子串T在主串S第pos个字符之后的位置
// 若不存在,则返回0;
int index_KMP(String S,String T,int *next)
{
int i=1;
int j=1;
// int next[255];
//get_next[T,next];
while(i<=S[0] && j<=T[0])
{
if(j==0||S[i]==T[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j>T[0])
{
return i-T[0];
}
else
{
return 0;
}
}
int main()
{
char s[255]=" aabbababaaaba";
char t[255]=" ababaaaba";
int next[255];
s[0]=13;
t[0]=9;
get_next(t,next);
printf("打印next[i]\n");
for(int i=1;i<10;i++)
{
printf("%d",next[i]);
}
printf("\n");
int index;
index=index_KMP(s,t,next);
printf("%d\n",index);
}
//2.KMP优化
#include<stdio.h>
#include<stdlib.h>
typedef char* String;
void get_next(String T,int *next)
{
int j=0;
int i=1;
next[1]=0;
while(i<T[0])
{
if(j==0||T[i]==T[j])
{
i++;
j++;
if(T[i]!=T[j])
{
next[i]=j;
}
else
{
next[i]=next[j];
}
}
else
{
j=next[j];
}
}
}
//若存在,则返回子串T在主串S第pos个字符之后的位置
// 若不存在,则返回0;
int index_KMP(String S,String T,int *next)
{
int i=1;
int j=1;
// int next[255];
// get_next[T,next,1];
while(i<=S[0] && j<=T[0])
{
if(j==0||S[i]==T[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j>T[0])
{
return i-T[0];
}
else
{
return 0;
}
}
int main()
{
char s[255]=" aabbababaaaba";
char t[255]=" ababaaaba";
int next[255];
s[0]=13;
t[0]=9;
get_next(t,next);
printf("打印next[i]:\n");
for(int i=1;i<10;i++)
{
printf("%d",next[i]);
}
printf("\n");
printf("子串在主串的位置是\n");
int index;
index=index_KMP(s,t,next);
printf("%d\n",index);
}
到这里,串的章节就整理完了。之后会更新栈和队列的相关知识。
2020.9.21.