一、引入
话说trie树是一切字符串上的高级数据结构的基础,那么KMP就是一切字符串匹配的基础。
所以我们来说说如何看毛片KMP。
二、正题
KMP算法要解决的问题就是在字符串(也叫主串)中的模式(pattern)定位问题。说简单点就是我们平时常说的关键字搜索。模式串就是关键字(接下来称它为P),如果它在一个主串(接下来称为T)中出现,就返回它的具体位置,否则返回-1(常用手段)。
虽然说这种问题用string库里的函数find()可以过,但KMP不仅仅是算法,更是一种很重要的思想。
先看一个栗子:P=“abcabcdaaabcabcd”,T=“abcabcabc”,求T在P中的位置。
1、匹配第一位
abcabcdaaabcabcd
abcabcabc
2、匹配第二位
abcabcdaaabcabcd
abcabcabc
3、匹配第三位
abcabcdaaabcabcd
abcabcabc
。。。
7、匹配第七位,失配
abcabcdaaabcabcd
abcabcabc
怎么办呢?
暴力:T串后移一位,重新开始(果然够暴力)
abcabcdaaabcabcd
abcabcabc
可是已经匹配了六位了,舍不得,怎么办?
仔细观察,哎!
(abc)1(abc)2daaabcabcd
(abc)3(abc)4abc
我们发现(abc)4也是T串的前缀((abc)3),而且在P串中已经匹配了((abc)2)
那么我们完全可以让(abc)3来和(abc)2匹配。
KMP:
abcabcdaaabcd
abcabcabc
三、失配指针
从上面描述的算法思想可以知道,KMP的关键在于失配之后T串怎么移动。
我们引入一个概念:失配指针f[i]。它表示T串1~f[i]位于i-f[i]+1~i位是相同的,那么如果T串第i位和P串第j位失配了,那么就让T串第f[i]位和P串第j位匹配。
通过失配指针我们很容易就能完成T串移动的任务。
那失配指针怎么求呢?
假设我们已知f[i-1],那么令j=f[i-1]。
1、如果T[j+1]==T[i],则f[i]=j+1。
2、如果T[j+1]!=T[i],则令j=f[j],递归下去。
代码如下:
1 void get_nxt(char *b) 2 { 3 int lenb=strlen(b+1); 4 nxt[1]=0; 5 for(int i=2,j=0;i<=lenb;i++) 6 { 7 while(j>0&&b[i]!=b[j+1]) 8 j=nxt[j]; 9 if(b[i]==b[j+1]) 10 j++; 11 nxt[i]=j; 12 } 13 }//nxt数组即为文中的f数组(别喷我懒)
再者就是匹配了。
设此时T串匹配到i,P串匹配到j,则有:
1、T[i+1]==p[j+1],则i++,j++。
2、T[i+1]!=P[j+1],则令i=f[i],递归下去,直到T[i+1]==p[j+1]。
代码如下:
1 for(int i=1,j=0;i<=lena;i++) 2 { 3 while(j>0&&a[i]!=b[j+1]) 4 j=nxt[j]; 5 if(a[i]==b[j+1]) 6 j++; 7 if(j==lenb) 8 { 9 printf("%d\n",i-lenb+1); 10 j=nxt[j]; 11 } 12 }
四、例题
这里就以洛谷 P3375【模板】KMP字符串匹配 为例
https://www.luogu.org/problemnew/show/P3375
注意这题最后还要输出nxt数组。
代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<vector> 8 using namespace std; 9 char a[1000010],b[1000010]; 10 int nxt[1000010]; 11 int main() 12 { 13 scanf("%s%s",a+1,b+1); 14 int lena=strlen(a+1),lenb=strlen(b+1); 15 nxt[1]=0; 16 for(int i=2,j=0;i<=lenb;i++) 17 { 18 while(j>0&&b[i]!=b[j+1]) 19 j=nxt[j]; 20 if(b[i]==b[j+1]) 21 j++; 22 nxt[i]=j; 23 } 24 for(int i=1,j=0;i<=lena;i++) 25 { 26 while(j>0&&a[i]!=b[j+1]) 27 j=nxt[j]; 28 if(a[i]==b[j+1]) 29 j++; 30 if(j==lenb) 31 { 32 printf("%d\n",i-lenb+1); 33 j=nxt[j]; 34 } 35 } 36 for(int i=1;i<=lenb;i++) 37 { 38 if(i!=1) 39 printf(" "); 40 printf("%d",nxt[i]); 41 } 42 return 0; 43 }