KMP算法
KMP是解字符串匹配这类题目的算法,又称“看毛片”算法。
如下图,给定一个长度为n的文本,给定一个长度为m 的字符串,求该字符串在给定文本的中出现的次数。KMP就是解决这一类题目的。
i 1
zzkzzzkzzzkzkkkz
zzkzzkzzkzzk
j 1
继续考虑上面的问题。首先我相信每个人都有一个笨蛋的想法,就是枚举在文本的起始位置i,然后把字符串从这个位置开始一个一个对比,如果遇到某一位j不相同就停止并i++,重新开始对比。这样的效率最坏的情况为O(m(n-1)),很不优秀。KMP其实就是基于这个笨蛋思想的优化。
i 6
a zzkzzzkzzzkzkkkz
b zzkzzkzzkzzk
j 5
我们可以假设当前对比文本位置i,字符串已经比较了j位,如果i和j+1不同其实我们不需要重新开始匹配,如果基于原来的基础之上,稍微将字符串往后移一移,这样j可以保留一定的位置,效率上得到提升。但是新位置还是要满足匹配,所以就要寻找一个最大的j’满足b[1..j’]和 a[i-j’+1..i]一致,然后继续用j’+1和i进行比较,如果不满足就继续进行以上过程。基本过程如下。
i 6
a zzkzzzkzzzkzkkkz
b zzkzzkzzkzzk
j 2 //2是能满足的最大j’,但是j’+1与i不匹配,所以继续
i 6
a zzkzzzkzzzkzkkkz
b zzkzzkzzkzzk
j 0
因为j之前已经匹配,所以寻找b[1..j’]和 a[i-j’+1..i]一致,其实就是寻找一段满足b[1..j’]和 b[j-j’+1..j]一致的自匹配,这个明显能够预处理出来,效率为O(n+m)。
主程序如下:
void KMP(){
int j=0;
for (int i=1;i<=n;i++){
while (j>0&&b[j+1]!=a[i]) j=f[j]; //f[j]就是预处理的数组,存最大的j’
if (b[j+1]==a[i]) j++;
if (j==m) printf("%d\n",i-m+1),j=f[j]; //完成匹配,输出
}
}
预处理f数组的过程与寻找匹配差不多,代码如下:
void work(){
int j=0;
for (int i=2;i<=m;i++){ //这里一定要从2开始,因为从1开始就全部匹配了,f[j]不能满足小于j
while (j>0&&b[j+1]!=b[i]) j=f[j];
if (b[j+1]==b[i]) j++;
f[i]=j;
}
}
KMP差不多就是这样,但是KMP存在有缺陷,一次只能拿一个字符串与文本比较,所以就有了AC自动机。