本cg在学习了kmp算法之后感叹道这个算法的奇妙之处,网上有很多关于其的原理,这里我就不再赘述了,但是直接了当的kmp代码确实是很少,所以为了帮助各位提高效率和自己巩固写了这篇文章。
首先还是说一下kmp算法是干什么的吧,kmp算法是用来找字串的,算是数据结构中的难点了,理解起来也比较费劲,有人就说了,学这干什么,不如直接substr。nonono,stl大法虽好,可不能贪杯啊,kmp算法即是高效找字串的方法,一般的方法发现不匹配之后就直接回到起始,大大降低了效率,但此算法不同,可以记住最近的匹配点,提高效率,好了,废话不多说,直接上代码
#include<iostream>
#include<string>
using namespace std;
int nexts[2000000];
void getnext(string s)
{
nexts[0]=0;
int i=1,j=0;
while(i<s.length())
{
if(j==0||s[j]==s[i])
nexts[++i]=++j;
else
j=nexts[j];
}
}
int nextval[200005];
void getnextval(string s)
{
int i=1;
nextval[0]=0;
while(i<s.length())
{
int index=nexts[i];
if(s[i]==s[index])
nextval[i]=nextval[index];
else
nextval[i]=nexts[i];
i++;
}
}
bool kmp(string s1,string s2)
{
int i=0,j=0;
getnext(s2);getnextval(s2);
while(i<s1.length()&&j<s2.length())
{
if(j==0||s1[i]==s2[j])
{
i++;j++;
}
else
{
j=nextval[j];
}
}
if(j>=s2.length())
return true;
else
return false;
}
int main()
{
int n;cin>>n;
string s1,s2;cin>>s1>>s2;
s1+=s1;
if(kmp(s1,s2))
cout<<"wow"<<endl;
else
cout<<"TAT"<<endl;
return 0;
}
如果是为了拿板子的话,可以润了,别忘了点个赞。
接下来,分析一下代码,kmp最关键的即使next数组和nextval数组的构建,浅显的讲一下next数组和nextval的构建,nextval可以理解为next的升级办,提高了原有的效率。
首先next数组,一般我们从下标为0开始,并将其定义为0.然后从字符串的下标为0,next数组的下标为1,开始,然后next数组的值即为字符串此下标前的字符相同前后缀的长度加1,比如aabaab,当前字符下标为5的时候,前面有五个字符,前后缀的最大相同长度为2,则此next数组的值为2+1=3。以此类推,因为第一个字符前面没有东西,所以将next【0】定义为0.
来看getnext
void getnext(string s)
{
nexts[0]=0;
int i=1,j=0;
while(i<s.length())
{
if(j==0||s[j]==s[i])
nexts[++i]=++j;
else
j=nexts[j];
}
}
记录i为next数组开始下标,j为next数组的值。如果j为0则意味着前方没有相匹配的,即是最坏的情况,若不为0,则直接在原有的基础上加一,至于为什么,可以看其他的深入讲解。如果两个都不满足的话,怎么办?那么直接就去看门牌(谐音kmp)即j直接变成以j为下标的next数组的值,最后不断循环,next数组就求出来了;
接下来看nextval
void getnextval(string s)
{
int i=1;
nextval[0]=0;
while(i<s.length())
{
int index=nexts[i];
if(s[i]==s[index])
nextval[i]=nextval[index];
else
nextval[i]=nexts[i];
i++;
}
}
nextval的原理即是如果在i位置的字符s[i]如果等于以next【i】的值为下标处的字符串中的字符的话,那么nextval【i】的值就是nextval【next【i】】的值,否则是自身的next【i】的值。
最后看总体kmp
bool kmp(string s1,string s2)
{
int i=0,j=0;
getnext(s2);getnextval(s2);
while(i<s1.length()&&j<s2.length())
{
if(j==0||s1[i]==s2[j])
{
i++;j++;
}
else
{
j=nextval[j];
}
}
if(j>=s2.length())
return true;
else
return false;
}
i和j分别源串和字串的下标,如果两个字符相匹配,则继续往下匹配,否则将子串的下标回溯到nextval的值,这就是我们求nextval的意义,用来回溯,避免重复计算。最后如果字串顺利循环完,那么说明匹配成功了,要么那就是失败了。自此,kmp算法就结束了,是不是还不算复杂啊,当然这只是将结论以代码的形式呈现出来,想要深刻了解,可以去看看视频。最后希望大家多给我这个ruog点赞-——来自大一cg的乞讨