浅谈KMP算法:
(大部分人的KMP写法都是不一样的)
一:
先给大家推荐一个讲kmp比较好理解的一个博客:阮一峰
二:
下面介绍一点相关概念:
栗子:
P串: ABCBD
前缀:A,AB,ABC,ABCB,ABCBD
真前缀:A,AB,ABC,ABCB
后缀:D,BD,CBD,BCBD,ABCBD
真后缀:D,BD,CBD,BCBD
KMP算法里的next数组的含义:
栗子:
P串: ABCDABD
next[] = {-1, 0, 0, 0, 0, 1, 2, 0, };
next[i] 的含义:P串前 i 个字符(包括第 i 个)的最长真前缀后缀公共长度;
如 i = 5时:
真前缀:A,AB,ABC,ABCD
真后缀:BCDA,CDA,DA,A
显而易见, 前缀和后缀相同的只有 A,而 A 的长度为 1,所以next[5] = 1;
next数组求法:
//用通俗的语句说就是k是用来表示子串中前k个和后k个是相同的,i是用来遍历数组 void get_next(char *t,int lent){ nex[0] = -1; for(int i = 0,k = -1;i < lent;){ if(k==-1||t[i] == t[k]){ ++k;++i; nex[i]=k; }else k = nex[k]; /*如果c[i]和c[k]中字符不同说明匹配是失败,要把k的值重新退到next[ k ] 直到两者相同才停止。这样做的好处是没必要再重新从头再来,节约时间*/ } }
简单KMP算法的实现:
//返回主串中匹配的位置(第一个),如果不匹配返回-1; int kmp(char *s,char *t,int lens,int lent) { int i = 0, j = 0; while(i < lens&&j<lent) { if(j==-1||s[i] == t[j]){ i++;j++; if(j==lent){ return i-j+1; } }else j=nex[j]; } return -1; }
几道例题:
洛谷P3375:
#include<cstdio> #include<cstring> using namespace std; const int maxn = 1e6 + 10; int nex[maxn]; char s[maxn],t[maxn]; int lens,lent; void get_next(){ nex[0] = -1; for(int i = 0,k = -1;i < lent;){ if(k==-1||t[i] == t[k]){ ++k;++i; nex[i]=k; }else k = nex[k]; } } void kmp() { int i = 0, j = 0; while(i < lens&&j<lent) { if(j==-1||s[i] == t[j]){ i++;j++; if(j==lent){ printf("%d ",i-j+1); j=nex[j]; } }else j=nex[j]; } } int main(){ while(~scanf("%s %s",s,t)){ lens=strlen(s); lent=strlen(t); get_next(); kmp(); for(int i=1;i<=lent;++i){ printf("%d%c",nex[i],i==lent?' ':' '); } } return 0; }