关于KMP和exKMP

又一次写字符串
学KMP都是寒假的事了结果现在才写总结hhhhh
其实KMP在学的时候就听得迷迷糊糊的,但因为代码好记所以也算会写吧23333现在学exKMP也算对KMP有了更深的理解吧
那些现在不太理解的东西可以先放一放,过段时间再抱起来啃可能会好一些
关于KMP
主要其实就是对next数组的理解
next数组 记录的是字符串匹配过程中失配情况下可以向前多跳几个字符
贴一个博客:
KMP入门博客
感觉写的非常详细了
关于KMP的calcnext 完全匹配后对于j的处理
如果上个匹配处结束的字符串可以出现在下个匹配中,则j=nxt[j]
如果每个匹配与之前的匹配不允许有交叉的字符,则j=0
贴几道KMP的模板题吧
poj 3461
就是一道模板题吧,也没有什么题解

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define N 1000010
using namespace std;

int nxt[N],len1,len2,tot;
char s1[N],s2[N];

void calcnext(){
    int i=0,j=-1;
    nxt[0]=-1;
    for(int i=0;i<len1;){
        if(j==-1||s1[i]==s1[j]){
            i++,j++;
            nxt[i]=j;
        }
        else j=nxt[j];
    }
}

void kmp(){
    int i=0,j=0;
    while(i<len1){
        if(j==-1||s1[i]==s2[j]){
            i++,j++;
            if(j==len2){
                tot++;
                j=nxt[j];
            }
        }
        else j=nxt[j];
    }
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        tot=0;
        memset(nxt,0,sizeof(nxt));
        scanf("%s",s2);     
        scanf("%s",s1);
        len1=strlen(s1);
        len2=strlen(s2);
        calcnext();
        kmp();
        printf("%d\n",tot);
    }
    return 0;
}

poj 2406
做这道题也是寒假了,那充满浓浓封建迷信气息的函数名23333
其实这道题主要就是对nxt数组的理解,计算有多少个循环节
【这道题的思想在hdu4333 一道求解exKMP的题中也有用到】
也算是积累一种求循环节的方法吧

#include<cstdio>
#include<cstring>
#define N 1000010
#define ms(x,y) memset(x,y,sizeof(x))
using namespace std;

char s[N];
int nxt[N],len;

void ac(){
    int i=0,j=-1;
    nxt[0]=-1;
    while(i<len){
        if(j==-1||s[i]==s[j]){
            i++,j++;
            nxt[i]=j;
        }
        else j=nxt[j];
    }
}

int main(){
    while(s[0]!='.'){
        ms(nxt,0);
        scanf("%s",s);
        if(s[0]=='.') break;
        len=strlen(s);
        ac();
        if(len%(len-nxt[len])==0){
            printf("%d\n",len/(len-nxt[len]));
        }
        else printf("1\n");
    }
    return 0;
}

关于exKMP
也是贴一个讲的很详细的PPT
exKMP的详解
也贴一个模板题
hdu 4333
算是一个很水的题解吧:
主要是注意对循环节的处理(这个上面有贴)
然后对大于等于小于的数分别进行三种处理
如果该点的匹配程度大于等于原长,那么生成的这个新数肯定跟原数是一样的
其他情况下
比较它们失配的那一位的大小关系(肯定一次就可以比较出来了) 然后对应的值++
[其实真实写这道题的时候用不着这么复杂,但因为想存个板所以写的复杂了一些]
[这个板对应上面的PPT看可能会理解地更透彻]

//就当存个exKMP模板 
#include<cstdio>
#include<cstring>
#define ms(x,y) memset(x,y,sizeof(x))
using namespace std;

const int N = 1000010;

int t;
char s1[N],s2[N*2];
int nxt[N];
int tot=0;

inline int Min(int a,int b){
    return a<b?a:b;
}

void getnxt(){
    int len=strlen(s1);
    int i=0,j=-1;
    nxt[0]=-1;
    while(i<len){
        if(j==-1||s1[i]==s1[j]){
            i++,j++;
            nxt[i]=j;}
        else j=nxt[j];
    }
}

//接下来是exkmp部分
int next[N],extand[N];

void getnext(){//起函数名废
    int i=0,po=1,len=strlen(s1);
    next[0]=len;//初始化nxt[0]
    while(s1[i]==s1[i+1]&&i+1<len) i++;
    next[1]=i;
    for(int i=2;i<len;i++){
        int p=po+next[po]-1,l=next[i-po];
        if(i-1+l<p) next[i] = l;//情况一 可直接求解 
        else{
            int j;
            if(p-i+1>0) j=p-i+1;
            else j=0;//如果i>po+next[po],则要从头开始匹配
            while(i+j<len&&s1[j]==s1[j+i]) j++;//计算next[i] 
            next[i]=j;
            po=i;
        }
    }
}

void getex(){
    getnext();
    int i=0,po=0;
    int slen=strlen(s2),tlen=strlen(s1);
    int minlen=Min(slen,tlen);
    while(i<minlen&&s1[i]==s2[i]) i++;
    extand[0]=i;
    for(int i=1;i<slen;i++){
        int p=po+extand[po]-1,l=next[i-po];
        if(i-1+l<p) extand[i]=l;
        else{
            int j;
            if(p-i+1>0) j=p-i+1;
            else j=0;
            while(i+j<slen&&j<tlen&&s2[j+i]==s1[j]) j++;
            extand[i]=j;
            po=i;
        }
    }
}

void update(){
    ms(nxt,0);ms(next,0);ms(extand,0);ms(s2,0);
}

int main(){
    scanf("%d",&t);
    while(t--){
        tot++;
        update();
        scanf("%s",s1);
        int len=strlen(s1);
        getnxt();
        int temp,k=len-nxt[len];
        if(len%k==0) temp=len/k;
        else temp=1;//求循环节
        for(int i=0;i<len;i++)
            s2[i]=s2[i+len]=s1[i];
        getex();
        int a=0,b=0,c=0;
        for(int i=0;i<len;i++){
            if(extand[i]>=len) a++;
            else if(s1[extand[i]]>s2[i+extand[i]]) b++;
            else if(s1[extand[i]]<s2[i+extand[i]]) c++;
        }
        printf("Case %d: %d %d %d\n",tot,b/temp,a/temp,c/temp);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值