KMP算法(学习笔记)

KMP用于线性时间内处理字符串匹配问题.

KMP算法的代码实现不难,难点在于对失配指针,也就是next数组的理解.

关于这个理解,各种神犇的博客都有很全面地分析,我就不再过多地赘述了,只结合代码简单分析一下.

首先,KMP算法就两个部分,而且还长得特别地像.

第一个部分是预处理,也就是模式串B(用来匹配的串)的"自我匹配"过程,这个过程是用来求next数组的.

第二个部分是B与主串A(等待匹配的串)的匹配过程.

我们用i,j指针表示\(A[i-j+1...i]\)\(B[1...j]\)完全相等.i是主串A的指针,它在不断地变化,随着i的增加,模式串B的指针j也应相应地变化,才能始终保证上述"\(A[i-j+1...i]\)\(B[1...j]\)完全相等".

那么指针j应该如何"相应地变化"呢?就引入了next数组.

\(next[j]\)记录的是当模式串B匹配到下标j位置时,不能再与主串A匹配下去(即当\(A[i+1]!=B[j+1]\)时),指针j能够向前跳到的(下标)最大位置.这个位置(假设下标是k),应当满足的条件是\(B[1...k]=B[j-k+1...j]\),简单来说就是既然我们不能保证B串的前j个都能匹配了,那么退而求其次,我可以保证B串的前k个(k<j)仍然匹配(如果这也是奢望,那就一直退而求其次,直到不能再退,即k=0)


int j=0;
nxt[1]=0;
//初始化:匹配到第一个位置不能匹配了,那就只能到0了
for(int i=1;i<n;i++){
    while(j>0&&s2[i+1]!=s2[j+1])j=nxt[j];
//不能继续匹配且j还没减到0,考虑退一步
    if(s2[i+1]==s2[j+1])j++;
//能匹配,j的值+1
    nxt[i+1]=j;
//记录第i+1个位置的失配指针位置
}

第二个部分就是由B串自己跟自己匹配变为B串与A串匹配,所以过程是极其相似的.

int j=0;
for(int i=0;i<m;i++){
    while(j>0&&s1[i+1]!=s2[j+1])j=nxt[j];
    if(s1[i+1]==s2[j+1])j++;
    if(j==n){
        printf("%d\n",i+1-n+1);
//模式串(子串)串首在主串(母串)中的位置
        ans++;
//模式串(子串)在主串(母串)中的出现的次数
        j=nxt[j];
//这里是为了让程序继续运行下去
//因为有可能后面还有合法的匹配.
    }
}

KMP模板(用上面这两段就能应付了)

扫描二维码关注公众号,回复: 5090794 查看本文章

[BOI2009]Radio Transmission 无线传输

题意:给你一个字符串,它是由某个字符串不断自我连接形成的.但是这个字符串是不确定的,现在只想知道它的最短长度是多少?

样例输入:

8

cabcabca

样例输出:

3

样例解释:abc,cab,bca都符合题意.

本题灵活运用到了next数组的性质:\(next[j]\)能够始终保证\(B[1...j]=B[i-j+1...i]\),本题的实质就是求字符串的循环节,所以j与\(next[j]\)的距离就是本题的答案.(前提是\(next[j]\)不为零),所以输出\(n-next[n]\)最可靠.

[POI2006] OKR-Periods of Words

本题难点在于读懂题意,题目意思就是给定一个字符串,对于该字符串的每一个前缀x,求该前缀x的 最长的前缀y,且满足x又是yy(相当于两个y合并)的前缀.答案就是所有的这些最长的且符合要求的前缀的长度之和.

根据next数组的性质,我们只要让失配指针一直失配下去,直到不能失配为止(即到达边界下标0处)

long long ans=0;//十年oi一场空...
for(int i=1;i<=k;i++){
    if(nxt[nxt[i]])
        nxt[i]=nxt[nxt[i]];
}
for(int i=1;i<=k;i++)
    if(nxt[i])ans+=i-nxt[i];
printf("%lld\n",ans);

小结一下,KMP算法的代码不难实现,但重点是能够理解next数组,失配指针的含义,从而能够得到很多性质.而大多数有关KMP算法的题目就是利用这些性质来巧妙解题的,代码量都不会很大.

猜你喜欢

转载自www.cnblogs.com/PPXppx/p/10326685.html
今日推荐