信息学奥赛一本通 提高篇 提高版 第二部分 字符串算法 第2章 KMP算法

信息学奥赛一本通 提高篇 提高版 第二部分 字符串算法 第2章 KMP算法

https://blog.csdn.net/starstar1992/article/details/54913261此文帮助读者快速了解KMP算法

https://blog.csdn.net/buppt/article/details/78531384此文帮助理解k=next[k]的回溯算法

https://blog.csdn.net/v_july_v/article/details/7041827此文帮助更深入的理解KMP算法

开始之前,先学习,可通过下面问题,学习KMP字符串匹配。

//P3375 【模板】KMP字符串匹配
//在线测评地址https://www.luogu.org/problemnew/show/P3375
//通用的做法,之后输出的结果,别忘了+1
//样例通过,提交,测试点11-13 WA。
//修改,int next[maxn];//之前写成 char next[maxn],错了11-13测试点。
//提交AC。2018-9-28
#include <stdio.h>
#include <string.h>
#define maxn 1000100
char s1[maxn],s2[maxn];//此处写成 char s1[maxn],s2[maxn],next[maxn];
int next[maxn];//之前写成 char next[maxn],错了11-13测试点。
void calNext(char *p,int len){
    int k=-1,q;
    next[0]=-1;
    for(q=1;q<len;q++){
        while(k>-1&&p[q]!=p[k+1])k=next[k];
        if(p[q]==p[k+1])k++;
        next[q]=k;
    }
}
void KMP(char *str,int len1,char *p,int len2){
    int k=-1,i;
    calNext(p,len2);
    for(i=0;i<len1;i++){
        while(k>-1&&str[i]!=p[k+1])k=next[k];
        if(str[i]==p[k+1])k++;
        if(k==len2-1){
            printf(“%d\n”,(i-len2+1)+1);
            k=-1;
            i=i-len2+1;
        }
    }
}
int main(){
    int len1,len2,i;
    scanf(“%s%s”,s1,s2);
    len1=strlen(s1);
    len2=strlen(s2);
    KMP(s1,len1,s2,len2);
    for(i=0;i<len2;i++)
        printf(“%d “,next[i]+1);
    printf(“\n”);
    return 0;
}

#10043 「一本通 2.2 例 1」剪花布条

//#10043. 「一本通 2.2 例 1」剪花布条
//在线测评地址https://loj.ac/problem/10043 
//样例通过,提交AC。2018-10-1 23:24
//在线测评地址https://vjudge.net/problem/HDU-2087
//样例通过,提交AC。2018-10-1 23:29 
#include <stdio.h>
#include <string.h>
#define maxn 1010
char s[maxn],p[maxn];
int next[maxn];
void cal_next(char *ptr,int len){
    int k=-1,i;
    next[0]=-1;
    for(i=1;i<len;i++){
        while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
        if(ptr[i]==ptr[k+1])k++;
        next[i]=k;
    }
}
void kmp(char *str,int len_s,char *ptr,int len_p){
    int k=-1,i,cnt=0;
    cal_next(ptr,len_p);//漏了该句 
    for(i=0;i<len_s;i++){
        while(k>-1&&str[i]!=ptr[k+1])k=next[k];
        if(str[i]==ptr[k+1])k++;
        if(k==len_p-1){
            k=-1;
            cnt++;
        }
    }
    printf(“%d\n”,cnt);
}
int main(){
    int len_s,len_p;
    while(scanf(“%s”,s)){
        len_s=strlen(s);
        if(len_s==1&&s[0]==’#’)break;
        scanf(“%s”,p);
        len_p=strlen(p);
        kmp(s,len_s,p,len_p);
    }
    return 0;
}

#10044 「一本通 2.2 例 2」Power Strings

//#10044. 「一本通 2.2 例 2」Power Strings
//在线测评地址https://loj.ac/problem/10035 
//在线测评地址https://vjudge.net/problem/POJ-2406 
//猜测与next有关,简单模拟,但没有信心
//搜索网络,基本确认与next有关,重新模拟
//a
//-1
//1-(-1+1)=1
//aa
//-1 0
//2-(0+1)=1
//aaa
//-1 0 1
//3-(1+1)=1
//abca
//-1 -1 -1 0
//4-(0+1)=3
//abcab
//-1 -1 -1 0 1
//5-(1+1)=3
//基本确认,子字符串长度=n-(next[n]+1)
//思路:模拟+找规律。
//样例通过,提交80分,测试点2,5WA
//看了测试点2,才发现理解有错误
//aabaaba
//-1 0 -1 0 1 2 3 
//7-(3+1)=3
//而答案却是7 
//此文写得不错https://blog.csdn.net/zz_ylolita/article/details/50650394值得学习,修改,提交,WA,0分
//仔细一看,测试语句竟然未删除,
//删除,提交AC。2018-10-3 18:28
//此题的理解,建议,还是从模拟角度入手。 
#include <stdio.h>
#include <string.h>
#define maxn 1000100
char s[maxn];
int next[maxn];
void cal_next(char *ptr,int len){
    int k=-1,i;
    next[0]=-1;
    for(i=1;i<len;i++){
        while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
        if(ptr[i]==ptr[k+1])k++;
        next[i]=k;
    }
}
int main(){
    int len,len_p,cnt;
    while(scanf(“%s”,s)){
        len=strlen(s);
        if(len==1&&s[0]==’.’)break;
        cal_next(s,len);
        len_p=len-(next[len-1]+1);
        if(len%len_p==0)cnt=len/len_p;
        else cnt=1;//此处写成 cnt=len/len_p+1
        printf(“%d\n”,cnt);
    }
    return 0;
}
 

#10045 「一本通 2.2 练习 1」Radio Transmission

//#10045. 「一本通 2.2 练习 1」Radio Transmission
//在线测评地址https://loj.ac/problem/10045
//此文讲解得不错https://blog.csdn.net/lingyan_BVB/article/details/81698527摘抄如下
//对一个长度为l的字符串来说,next[l]记录的就是他最长的相同的前缀与后缀,也就是说有next[l]的长度是重复出现的,那么除去这个前缀以后,剩下的就是这个字符串最小的循环节了,即l-next[l]
//样例通过,提交AC。2018-10-3 20:08
//该题,本已很接近题解了,只是next笔算不过关,
//该题,与 #10044. 「一本通 2.2 例 2」Power Strings 解法基本雷同。 
#include <stdio.h>
#define maxn 1000100
char s[maxn];
int next[maxn];
void cal_next(char *ptr,int len){
    int i,k=-1;
    next[0]=-1;
    for(i=1;i<len;i++){
        while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
        if(ptr[i]==ptr[k+1])k++;
        next[i]=k;
    }
}
int main(){
    int L;
    scanf(“%d%s”,&L,s);
    cal_next(s,L);
    printf(“%d\n”,L-(next[L-1]+1));
    return 0;

#10046 「一本通 2.2 练习 2」OKR-Periods of Words

//#10046. 「一本通 2.2 练习 2」OKR-Periods of Words
//在线测评地址https://loj.ac/problem/10046 
//该题的难点,还是在于next数组的理解。2018-10-4 10:22  AC 
#include <stdio.h>
#include <string.h>
#define maxn 1000100
char s[maxn];
int next[maxn];
void cal_next(char *ptr,int len){
    int i,k=-1;
    next[0]=-1;
    for(i=1;i<len;i++){
        while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
        if(ptr[i]==ptr[k+1])k++;
        next[i]=k;
    }
}
int main(){
    int len,i;
    long long ans=0;
    scanf(“%d%s”,&len,s);
    cal_next(s,len);
    for(i=0;i<len;i++)
        if(next[i]>-1&&next[next[i]]>-1)
            next[i]=next[next[i]];
    for(i=0;i<len;i++)
        if(next[i]>-1)
            ans+=i-(next[i]);
    printf(“%lld\n”,ans);
    return 0;
}

#10047 「一本通 2.2 练习 3」似乎在梦中见过的样子

//#10047. 「一本通 2.2 练习 3」似乎在梦中见过的样子
//在线测评地址https://loj.ac/problem/10047 
//https://blog.csdn.net/u012288458/article/details/47272493此文代码写得不做,可以借鉴 
//该题难在样例的理解
//样例1简化版 
//aaaa 
//1
//aaa   第1种 
//aaaa  第2种 
// aaa  第3种 
//共计3种
//样例2简化版 
//abcabc
//2
//abcab     第1种 
// bcabc    第2种 
//共计2种 
//KMP中j的两次初始化,要特别小心。样例通过,提交AC。2018-10-4 20:53 
#include <stdio.h>
#include <string.h>
char s[15100];
int k,len,next[15100],ans=0;
void KMP(int p){
    int i,j;
    //j=p-1;//源自 p=0时,j=-1
    next[p]=p-1;
    for(i=p+1;i<len;i++){
        j=next[i-1];//漏了此句 
        while(j>p-1&&s[i]!=s[j+1])j=next[j];
        if(s[i]==s[j+1])j++;
        next[i]=j;
    }
    j=next[p];//漏了此句 
    for(i=p+1;i<len;i++){
        //j=p-1;
        while(j>p-1&&s[i]!=s[j+1])j=next[j];
        if(s[i]==s[j+1])j++;
        while((j-p+1)*2>=(i-p+1))j=next[j];
        if((j-p+1)>=k)ans++;
    } 
}
int main(){
    int i;
    scanf(“%s%d”,s,&k);
    len=strlen(s);
    for(i=0;i<len;i++)KMP(i);
    printf(“%d\n”,ans);
    return 0;

#10048 「一本通 2.2 练习 4」Censoring

//#10048. 「一本通 2.2 练习 4」Censoring
//在线测评地址https://loj.ac/problem/10048
//在线测评地址https://www.luogu.org/problemnew/show/P4824 
//样例通过,提交AC。2018-10-4 15:50
//以下为程序执行过程,希望能有助于读者理解。
 

《信息学奥赛一本通 提高篇 提高版 第二部分 字符串算法 第2章 KMP算法》

#include <stdio.h>
#include <string.h>
#define maxn 1000100
char S[maxn],T[maxn],W[maxn];
int top=-1,next[maxn],sp[maxn];
void cal_next(char *ptr,int len){
    int i,k=-1;
    next[0]=-1;
    for(i=1;i<len;i++){
        while(k>-1&&ptr[i]!=ptr[k+1])k=next[k];
        if(ptr[i]==ptr[k+1])k++;
        next[i]=k;
    }
}
void done(char *str,int lens,char *ptr,int lenp){
    int i,j;
    for(i=0;i<lens;i++){
        W[++top]=str[i];
        if(top>0)j=sp[top-1];//此处写成 if(top>=0)
        else j=-1;
        while(j>-1&&W[top]!=ptr[j+1])j=next[j];
        if(W[top]==ptr[j+1])j++;
        sp[top]=j;
        if(sp[top]==lenp-1){//此处写成 if(top==lenp-1)
            top-=lenp;
        }
    }
    W[top+1]=0;
    printf(“%s”,W);
}
int main(){
    int len_S,len_T;
    scanf(“%s%s”,S,T);
    len_S=strlen(S),len_T=strlen(T);
    cal_next(T,len_T);
    done(S,len_S,T,len_T);
    return 0;
}
2018-10-4 20:54 AC该节内容。该节的核心内容在于 next的理解及灵活应用。

 

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/mrcrack/article/details/82874007
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞