kmp常见用法

1.字符串匹配
例题:「POJ3461」Oulipo 「HDU2087」剪花布条 「USACO2015FEB」Censoring (Silver)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;

char s[N],t[N];
int lens,lent;
int stk[N],top;
int kmp[N],f[N];

int main(){
    scanf("%s%s",s+1,t+1);
    lens=strlen(s+1);
    lent=strlen(t+1);
    kmp[1]=0;
    for(int i=2,j=0;i<=lent;i++){
        while(j&&t[i]!=t[j+1])j=kmp[j];
        if(t[i]==t[j+1])j++;
        kmp[i]=j;
    }
    for(int i=1,j=0;i<=lens;i++){
        while(j&&s[i]!=t[j+1])j=kmp[j];
        if(s[i]==t[j+1])j++;
        f[i]=j;
        stk[++top]=i;
        if(j==lent){
            top-=lent;
            j=f[stk[top]];
        }
    }
    for(int i=1;i<=top;i++)
        printf("%c",s[stk[i]]);
    return 0;
}
Censorting
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;

char a[N],b[N];
int nex[N];

int main(){
    while(1){
        cin>>a+1>>b+1;;
        if(a[1]=='#') break;
        memset(nex,0,sizeof nex);
        int lena=strlen(a+1),lenb=strlen(b+1),ans=0;
        for(int i=2,j=0;i<=lenb;i++){
            while(j>0&&b[i]!=b[j+1])j=nex[j];
            if(b[i]==b[j+1])j++;
            nex[i]=j;
        }
        for(int i=1,j=0;i<=lena;i++){
            while(j>0&&(j==lenb||a[i]!=b[j+1]))j=nex[j];
            if(a[i]==b[j+1])j++;
            if(j==lenb){
                ans++;
                i=i+lenb-1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
剪花布条

2.最小循环节
例题:「POJ2406」Power Strings 「POJ1961」Period 「BalticOI2009」Radio Transmission

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+6;

char a[N];
int kmp[N],len;

void calc_kmp(){
    kmp[1]=0;
    for(int i=2,j=0;i<=len;i++){
        while(j>0&&a[i]!=a[j+1])j=kmp[j];
        if(a[i]==a[j+1])j++;
        kmp[i]=j;
    }
}

int main(){
    while(1){
        scanf("%s",a+1);
        if(a[1]=='.')return 0;
        len=strlen(a+1);
        calc_kmp();
        if(len%(len-kmp[len])==0&&len/(len-kmp[len])>1)
            printf("%d\n",len/(len-kmp[len]));
        else printf("1\n");
    }
}
Power Strings
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+6;

int n,t,kmp[N];
char a[N];

void calc_kmp(){
    kmp[1]=0;
    for(int i=2,j=0;i<=n;i++){
        while(j>0&&a[i]!=a[j+1])j=kmp[j];
        if(a[i]==a[j+1])j++;
        kmp[i]=j;
    }
}

int main(){
    while(scanf("%d",&n)&&n){
        scanf("%s",a+1);
        calc_kmp();
        printf("Test case #%d\n",++t);
        for(int i=2;i<=n;i++)
            if(i%(i-kmp[i])==0&&i/(i-kmp[i])>1)
                printf("%d %d\n",i,i/(i-kmp[i]));
        printf("\n");
    }
    return 0;
}
Period
#include<bits/stdc++.h>
#define inf 2000000000
#define ll long long 
#define mod 1000000007
using namespace std;

int n;
char a[1000005];
int kmp[1000005];

int main(){
    scanf("%d%s",&n,a+1);
    int j=0;
    for(int i=2;i<=n;i++){
        while(a[j+1]!=a[i]&&j)j=kmp[j];
        if(a[j+1]==a[i])j++;
        kmp[i]=j;
    }
    printf("%d",n-kmp[n]);
    return 0;
}
Radio Transmission

3.既是前缀又是后缀的子串
例题:「POJ2752」Seek the Name, Seek the Fame

#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+5;

char s[N];
int n,kmp[N],ans[N];

void getkmp(){
    int j=-1,i=0;
    kmp[0]=-1;
    while(i!=n){
        if(j==-1||s[i]==s[j])kmp[++i]=++j;
        else j=kmp[j];
    }
}

int main(){
    while(scanf("%s",s)!=EOF){
        n=strlen(s);
        getkmp();
        int cnt=0;
        for(int i=n;i!=-1;i=kmp[i])
            ans[++cnt]=i;
        for(int i=cnt-1;i>=1;i--)
            printf("%d ",ans[i]);
        printf("\n");
    }
    return 0;
}
Seek

4.求不相交前后缀匹配数量
例题:「NOI2014」动物园

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
const int N = 1e6+5;

int n,kmp[N],num[N];
char s[N];

int main(){
    scanf("%d",&n);
    while(n--){
        scanf("%s",s+1);
        int len=strlen(s+1),ans=1;
        kmp[1]=0;num[1]=1;
        for(int i=2,j=0;i<=len;i++){
            while(j>0&&s[i]!=s[j+1])j=kmp[j];
            if(s[i]==s[j+1])j++;
            kmp[i]=j;
            num[i]=num[j]+1;
        }
        for(int i=1,j=0;i<=len;i++){
            while(j>0&&s[i]!=s[j+1])j=kmp[j];
            if(s[i]==s[j+1])j++;
            while(j>i/2)j=kmp[j];
            //printf("%d ",num[j]);
            ans=(1ll*ans*(num[j]+1))%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}
Zoo

5.求字符串‘周期’
例题:[POI2006]OKR-Periods of Words

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000010;

char s[N];
int n,fail[N];

int main(){
    scanf("%d%s",&n,s+1);
    ll ans=0;fail[1]=0;
    for(int i=2,j=0;i<=n;i++){
        while(j&&s[i]!=s[j+1])j=fail[j];
        if(s[i]==s[j+1])j++;
        fail[i]=j;
    }
    for(int i=1;i<=n;i++){
        int j=i;
        while(fail[j])j=fail[j];
        if(fail[i])fail[i]=j;
        ans+=i-j;
    }
    printf("%lld",ans);
    return 0;
}
OKR Periods of Words

6.后缀的前缀 和 字符串的前缀 的 最大公共
例题:【模板】扩展KMP(Z函数) [CF126B] Password

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =2e7+2;

char a[N],b[N];
ll z[N],p[N];
ll lena,lenb,ans;

int getsame(){
    int cnt=0,num=max(lena,lenb);
    for(int i=1;i<=num;i++){
        if(a[i]==b[i])cnt++;
        else return cnt;
    }
}

void getz(){
    z[1]=lenb;
    int l=0,r=0;
    for(int i=2;i<=lenb;i++){
        if(i<=r)z[i]=min(z[i-l+1],(ll)r-i+1);
        while(z[i]+i<=lenb&&b[z[i]+i]==b[z[i]+1])z[i]++;
        if(i+z[i]-1>r)r=i+z[i]-1,l=i;
    }
}

void getp(){
    p[1]=getsame();
    int l=0,r=0;
    for(int i=1;i<=lena;i++){
        if(i<=r)p[i]=min(z[i-l+1],(ll)r-i+1);
        while(p[i]+i<=lena&&a[p[i]+i]==b[p[i]+1])p[i]++;
        if(i+p[i]-1>r)r=i+p[i]-1,l=i;
    }
}

int main(){
    scanf("%s%s",a+1,b+1);
    lena=strlen(a+1),lenb=strlen(b+1);
    getz();
    for(int i=1;i<=lenb;i++)ans^=(i*(z[i]+1));
    printf("%lld\n",ans);
    ans=0;getp();
    for(int i=1;i<=lena;i++)ans^=(i*(p[i]+1));
    printf("%lld\n",ans);
    return 0;
}
扩展kmp

password

7.前缀出现次数
例题:[CF432D] Prefixes and Suffixes

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;

char s[N];
int kmp[N],num[N],len,tot;
vector<pair<int,int> >ans;

int main(){
    scanf("%s",s+1);
    len=strlen(s+1);
    kmp[1]=0;
    for(int i=2,j=0;i<=len;i++){
        while(j&&s[i]!=s[j+1])j=kmp[j];
        if(s[i]==s[j+1])j++;
        kmp[i]=j;
    }
    for(int i=1;i<=len;i++)num[i]++;
    for(int i=len;i>=1;i--)num[kmp[i]]+=num[i];

    for(int k=len;k;k=kmp[k]){
        tot++;
        ans.push_back(make_pair(k,num[k]));
    }
    printf("%d\n",tot);
    for(int i=(int)ans.size()-1;i>=0;i--)
        printf("%d %d\n",ans[i].first,ans[i].second);
    return 0;
}
Prefixes and Suffixes

 

posted @ 2021-02-01 13:22  _Famiglistimo  阅读(114)  评论(0编辑  收藏  举报