最近在打字符串,自然也就打到了kmp
kmp算法是由D.E. Knuth、J.H.Morris和V.R. Pratt提出的虽然两位都不认识orz
kmp算法是用来比较字符串是否相同,也就是给定两个字符串,A串和B串,需要回答B串是否为A串字串,我们一般把等待匹配的字符串称之为主串,来匹配的字符串称为模式串
按照朴素算法来解肯定是很简单的,在A串中不断枚举一个开始节点,若是不匹配就世界退出并更换开始节点,设A,B串长度分别为m,n,这里没有给出代码就是懒
虽然有最好情况,在开始的时候就能匹配到,时间复杂度为O(n),但也会存在排除最后才能找到的情况,这时算法时间复杂度会退化为O(mn),这就有点难以接受了。。。
然而kmp算法就比较神奇,可以在O(n)的时间内跑完整个代码,非常稳定
那么这个算法到底怎么实现呢我也不知道简单来说,就是在A串枚举一个i,B串枚举一个j,看A[i]和B[j]是否匹配,如果匹配,则i和j都往后移一位,如果不匹配,则说明A串从1到i的字串和B串从1到j的字串并不匹配,我们需要在A串的1~i中枚举一个k,使A串的头k个字符和末尾k个字符匹配,使k越大越好(匹配的尽量长),再去跟B串匹配
其实kmp也可以算是朴素算法的进化版啦
模拟一遍
A a b a b a b a c a b a c a b a b a b
B a b a b a b
匹配到5时,我们发现了差异,A[5]=a,但是B[5]=b啊!
朴素算法的思考方式是
欸欸欸?不匹配?那行我从A串的第2个开始枚举起,那么我再来跑一遍
kmp的思维方式是
不匹配?观察A[1]~A[5],我们可以发现字串A[1]~A[3]和A[3]~A[5]是可以匹配的,那么我们将j换为4,i继续推进到6
知道找到最后我们才能找到答案
值得一提的是,如果是在不知道怎么读入字符串的同学,可以用cin和char(本蒟蒻就用的cin和charQAQ
挂个板子题
https://www.luogu.org/problem/P3375
emmm贴代码
#include<bits/stdc++.h> using namespace std; int nxt[1000010],A,B,j; char a[1000010],b[1000010]; int main(){ cin>>(a+1)>>(b+1); A=strlen(a+1);B=strlen(b+1); for (int i=2;i<=B;i++){ while(j&&b[i]!=b[j+1])j=nxt[j]; if(b[j+1]==b[i])j++; nxt[i]=j; } j=0; for(int i=1;i<=A;i++){ while (j>0&&b[j+1]!=a[i]) j=nxt[j]; if (b[j+1]==a[i]) j++; if (j==B) {printf("%d ",i-B+1);j=nxt[j];} } for (int i=1;i<=B;i++) printf("%d ",nxt[i]); return 0; }