假设现在我们面临这样一个问题:有一个文本串t,和一个模式串s,现在要查找s在t中出现的次数,怎么查找呢?
假如用暴力算法,那复杂度必为O(ls*lp)看起来确实不太靠普
所以 KMP 算法诞生了
首先举个栗子
.
第一位看似没有生么问题,但继续往下
就不匹配了,怎么办呢?
因为前三位是匹配的,于是我们要利用好这一点.
于是三位神仙(Knuth–Morris–Pratt)想出:
我们可以找前三位的最大既是前缀又是后缀的串,
在这里就是aba,偏又匹配了.
这样
有人也许会问,这样的串不止一个呀,穷举吗?
回答,不是,
只用跳转到 最长的那一个即可.
因为其它更短的既是前缀又是后缀的串一定是最长串的既是前缀又是后缀的串!
比如给定模式串DABCDABDE,我们很顺利的求得字符D之前的“DABCDAB”的各个子串的最长相同前缀后缀的长度分别为0 0 0 0 1 2 3,但当遍历到字符D,要求包括D在内的“DABCDABD”最长相同前缀后缀时,我们发现pj处的字符D跟pk处的字符C不一样,换言之,前缀DABC的最后一个字符C 跟后缀DABD的最后一个字符D不相同,所以不存在长度为4的相同前缀后缀。
怎么办呢?既然没有长度为4的相同前缀后缀,咱们可以寻找长度短点的相同前缀后缀,最终,因在s0处发现也有个字符D,s0=sj,所以nxt[j]=1.
递归代码如下
n=strlen(s+1);
for(int i=2,j=0;i<=n;i++)
{
while(j>0 && s[j+1]!=s[i]) j=nxt[j];
if(s[j+1]==s[i]) j++;
nxt[i]=j;
}
nxt数组预处理完毕后只需很短代码便可搞定
m=strlen(t+1);
for(int i=1,j=0;i<=m;i++)
{
while(j>0 && s[j+1]!=t[i]) j=nxt[j];
if(s[j+1]==t[i]) j++;
if(j==n)
{
ans++;
j=nxt[j];
}
}
代码中j=nxt[j]是为了找到更多的答案。
我们来分析一下复杂度
因为整个过程中,每次while循环都会是j减小,而另外改变y的值的地方只回把j增加1,故j最多减小n次,也就是复杂度为O(m)的再加上预处理的O(n),总复杂度为
O(m+n)
大家看完后各以
先做一下poj上这个来练习一下nxt数组
练练手
麻烦各位大佬不要随意转载,谢谢