擴展kmp模板

 

目的:找出字符串S的全部後綴與字符串T的最長公共前綴,預處理Next[i]。ios

   S字符串長度爲n,T字符串長度爲m。c++

   Next[i],i∈[0,m),表示T的後綴i與T的最長公共前綴。數組

   extend[i],i∈[0,n),表示T與S[i,n)的最長公共前綴。若是有一個extend[i]=m,則T在S中在i位置出現。ide

時間複雜度:O(|S|+|T|)函數

自用模板:加密

#include <iostream>
#include <string.h>
using namespace std;
const int maxn=1100;
int Next[maxn],extend[maxn];
void get_Next(char *s)
{
    int n=strlen(s);
    int i,j,k;
    for(j=0;1+j<n&&s[j]==s[1+j];j++);
    Next[1]=j;
    k=1;
    for(i=2;i<n;i++)
    {
        int len=k+Next[k],L=Next[i-k];
        if(L<len-i)
        {
            Next[i]=L;
        }
        else
        {
            for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++);
            Next[i]=j;
            k=i;
        }
    }
    Next[0]=n;
}
void ex_kmp(char *T,char *s)
{
    int n=strlen(T),m=strlen(s);
    int i,j,k;
    for(j=0;j<n&&j<m&&T[j]==s[j];j++);
    extend[0]=j;
    k=0;
    for(i=1;i<n;i++)
    {
        int len=k+extend[k],L=Next[i-k];
        if(L<len-i)
        {
            extend[i]=L;
        }
        else
        {
            for(j=max(0,len-i);j<m&&i+j<n&&s[j]==T[i+j];j++);
            extend[i]=j;
            k=i;       
        }
    }
        
}
//定義母串爲s,字串爲T,s的長度爲n,T的長度爲m,求T與S的每個後綴的最長公共前綴
//設extend數組,extend[i]表示T與S[i,n-1]的最長公共前綴,i從0到n-1,若是有一個extend[i]=m,則T在S中在i位置出現。
int main() 
{
    char s[maxn],T[maxn];
    cin>>T>>s;  //找出T的全部後綴與s的最長公共前綴 
    get_Next(s);
    ex_kmp(T,s);
    for(int i=0;i<strlen(T);i++)
    {
        cout<<extend[i]<<" ";
    }
    cout<<endl;
    return 0;
}
View Code

修改模板:spa

①配合fail數組進行去重code

 int len2=len-fail[len-1]-1;若是len%len2!=0則沒必要去重,再把len賦值給len2便可。如123123123須要去重,12312312則不須要。blog

 fail數組:設j=fail[i],知足s0...j=sj-i...i到si的最大的j ci

len=8 a b b
fail[] -1 0 -1 0 -1 0 1 2

 

 

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=100050;
void getfail(char *p,int *fail)
{
    int match=-1;
    fail[0]=-1;
    for(int i=1;p[i];i++)
    {
        while(match>=0&&p[match+1]!=p[i])
        {
            match=fail[match];
        }
        if(p[match+1]==p[i])
        match++;
        fail[i]=match; 
    }
}

int main()
{
    char s[maxn];
    int fail[maxn];
    cin>>s;  //對s進行去重,len2是去重後長度 
    getfail(s,fail);
    int len=strlen(s),len2;
    len2=len-fail[len-1]-1;
    if(len%len2!=0)len2=len;
    cout<<len2<<endl;
    return 0;
}
View Code

 

②修改get_Next函數

Next[i]表示Ti...len-1與T的最長公共前綴,作了一個return max的修改

len=9 a a a b a a a a b
Next[] 9 2 1 0 3 4 2 1 0

 

 

 

int get_Next(char *s)
{
    int n=strlen(s),ans=0;
    int i,j,k;
    for(j=0;1+j<n&&s[j]==s[1+j];j++);
    Next[1]=j;
    k=1;
    for(i=2;i<n;i++)
    {
        int len=k+Next[k],L=Next[i-k];
        if(L<len-i)
        {
            Next[i]=L;
        }
        else
        {
            for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++);
            Next[i]=j;
            k=i;
        }
        if(Next[i]>ans)
        ans=Next[i];
    } 
    Next[0]=n;
    return ans;
}
View Code

 

hdu4300,給加密方式,再給密文+部分或者所有明文,輸出密文+明文,將給的串所有解密or加密,而後求兩串先後綴相同部分,便可找出密文長度

我總以爲這題是在演我

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int Next[maxn],extend[maxn];
void get_Next(char *s)
{
    int n=strlen(s);
    int i,j,k;
    for(j=0;1+j<n&&s[j]==s[1+j];j++);
    Next[1]=j;
    k=1;
    for(i=2;i<n;i++)
    {
        int len=k+Next[k],L=Next[i-k];
        if(L<len-i)
        {
            Next[i]=L;
        }
        else
        {
            for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++);
            Next[i]=j;
            k=i;
        }
    }
    Next[0]=n;
}
int ex_kmp(char *T,char *s)
{
    int n=strlen(T),m=strlen(s),res=m;  //res=0而後wa了不少發,明文可能沒有 
    int i,j,k;
    for(j=0;j<n&&j<m&&T[j]==s[j];j++);
    extend[0]=j;
    k=0;
    for(i=1;i<n;i++)
    {
        int len=k+extend[k],L=Next[i-k];
        if(L<len-i)
        {
            extend[i]=L;
        }
        else
        {
            for(j=max(0,len-i);j<m&&i+j<n&&s[j]==T[i+j];j++);
            extend[i]=j;
            k=i;       
        }
        if(extend[i]+i>=n&&i>=extend[i])return i;
    }
    return res;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        char s[maxn],t[maxn],ch[30];
        int mp[30],lens;
        scanf("%s",ch);
        for(int i=0;i<26;i++)mp[ch[i]-'a']=i;
        
        scanf("%s",s);
        lens=strlen(s);
        for(int i=0;i<lens;i++)
        {
            t[i]=mp[s[i]-'a']+'a';
        }
        t[lens]='\0';
        get_Next(t);
        int place=ex_kmp(s,t);//s的全部後綴與t的最長公共前綴 
        for(int i=0;i<place;i++)printf("%c",s[i]);
        for(int i=0;i<place;i++)printf("%c",mp[s[i]-'a']+'a');
        printf("\n");
    } 
    return 0;
} 
View Code

 fzu1901,找知足S[i]=S[i+P]的全部i,卡常

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stack>
using namespace std;
const int maxn=1e6+10;
int Next[maxn],extend[maxn];
void get_next(char *s)
{
    int n=strlen(s),i,j,k;
    for(j=0;1+j<n&&s[j]==s[1+j];j++);
    Next[1]=j;
    k=1;
    for(i=2;i<n;i++)
    {
        int len=k+Next[k],L=Next[i-k];
        if(L<len-i)Next[i]=L;
        else
        {
            for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++);
            Next[i]=j;
            k=i;
        }
    }
}

int a[maxn];
int main()
{
    int T,t=1;
    scanf("%d",&T);
    while(T--)
    {
        char ch[maxn];
        scanf("%s",ch);
        get_next(ch);
        int tmp=0,len=strlen(ch);
        for(int i=0;i<len;i++)
        {
            if(Next[i]+i==len)
                a[tmp++]=i;
        }

        printf("Case #%d: %d\n",t++,tmp+1);
        for(int i=0;i<tmp;i++)
        {
            printf("%d ",a[i]);
        }
        printf("%d\n",len);
    }
    return 0;
}
View Code

 

 

...

相關文章
相關標籤/搜索