KMP——字符串匹配

声明
本文主要是一些代码,加一些例题,基本为模板,仔细内容讲解会给出其他博主的链接
如果想要知道一个字符串是否在另一个字符串中出现过,那么首先想到的就是暴力枚举

#include #include #include #include #include using namespace std; string s1,s2; int baolipipei() { int len1=s1.size() ; int len2=s2.size() ; int i=0,j=0; while(i>s1>>s2; cout<

但是,在两个字符串都很长的情况下,时间复杂度就很大,因此,给出一种算法KMP,可以解决字符串匹配等一类问题,在说KMP之前呢,首先说一下KMP用的一个数组,next数组(名字先不要在意)。next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。
那么如何求这个数组呢,暴力可以,但是复杂度过大
里面有详细的讲解,各位可以参考一下,主要还是理解之后大家记模板,也比较好记
https://blog.csdn.net/buppt/article/details/78531384
下面给出代码:
#include #include #include #include #include using namespace std; const int maxn=1005; string s; int next[maxn]; //最长前后缀相等 void GetNext() { int i,j=-1; next[0]=-1; //初始化为j=-1,next[0]=-1 for(int i=1; i>s; GetNext(); for(int i=0; i

现在呢开始进行匹配,我们知道,如果用暴力,当匹配到不正确的时候i会回到当前要查找的模式串(pattern)开头在对应的文本串(text)的位置下一位,那么根据next数组的含义,就可以直接回退到离当前j最近的j’使text[i] = pattern[j’+1] 能够成立。
这应该是博客上比较好的讲解kmp算法的了(可以看一看):
https://blog.csdn.net/v_july_v/article/details/7041827
KMP算法的一般思路:
① 初始化j=-1,表示pattern当前已被匹配的最后位。
②让i遍历文本串text,对每个i,执行③,④来试图匹配text[i]和pattern[j+1]。
③不断令j=next[j],直到j回退到-1,或者text[i] = pattern[j+1] 成立。
④如果text[i] = pattern[j+1] ,令j++。如果j达到pattern模式串的长度-1,说明pattern是text的子串。
代码如下:
#include #include #include #include using namespace std; const int maxn=1005; string s1,s2; int next[maxn]; void GetNext() {//计算pattern的next数组 int i,j=-1; next[0]=-1; for(int i=1; i>s1>>s2; if(KMP()) { cout<<"Yes\n"; } else cout<<"No\n"; }

看到了求next数组和字符串匹配的代码后,发现他们惊人的相似,事实上:求解next数组的过程就是模式串自己匹配的过程。
接下来,怎么用KMP来统计子串出现的次数呢?
暴力的思路肯定还是回到开头,但是知道了上面的内容,我们回溯肯定就用next[j]了。
代码如下:
#include #include #include #include #include using namespace std; const int maxn=1005; int next[maxn]; string s1,s2; void GetNext() { int j=-1; next[0]=-1; for(int i=1; i>s1>>s2; cout<

next优化:
当next[j]表示的串的j+1位失配时,就要继续进行回退,那么如果还失配,同样的还要回退,我们最开始做next数组的意义不就是减少无用功吗?因此改变了一下next数组存放的内容,让失配时回退一次就行了。那么现在,我们把优化后的next数组几位nextvel数组,他失去了next数组的最长前后缀相等的含义,现可理解为当模式串pattern的i+1位失配时,i应该会退的最佳位置。
代码如下:
#include #include #include #include using namespace std; const int maxn=1005; string s; int nextvel[maxn]; //nextvel[i]的含义应理解为当模式串的i+1完全失配时,i应当退回的最佳位置 j void GetNextvel() { int j=-1; nextvel[0]=-1; for(int i=1; i>s; for(int i=0; i

nextvel数组可以直接替换掉KMP中的next数组
直接套模板的几道题:
http://poj.org/problem?id=3461
#include #include #include #include #include using namespace std; const int maxn1=1e6+5; const int maxn2=1e4+5; char s2[maxn2],s1[maxn1]; int next[maxn2]; int len1,len2; void GetNext() { int j=-1; next[0]=-1; for(int i=1; i

【KMP——字符串匹配】http://poj.org/problem?id=2752
这是直接利用next数组的题目,需要的就是最大前后缀,而next数组刚好有这个意思
#include #include #include #include #include using namespace std; const int maxn=400005; int next[maxn]; int shu[maxn]; char s[maxn]; void GetNext(int len) {//求出每一位前缀后缀最大,用next记录 int j=-1; next[0]=-1; for(int i=1; i=0; i--){//反向输出 printf("%d",shu[i]); if(i) printf(" "); } printf("\n"); } }

http://caioj.cn/problem.php?id=1177
#include #include #include #include #include #include #include #include #include #include using namespace std; typedef long long LL; const int maxn=1005; int nextvel[maxn]; string s1,s2; int len1,len2; void GetNextvel() { int j=-1; nextvel[0]=-1; for(int i=1; i>s1>>s2; len1=s1.size() ; len2=s2.size() ; KMP(); }

最后,其实我个人觉得一些算法模板直接记录下来,能够熟练的打下来也很厉害了(小声说)

    推荐阅读