[kuangbin带你飞]专题十六 KMP & 扩展KMP

觉得自己的kmp写的太丑了,但是又习惯了这么写了。。。

A - Number Sequence HDU - 1711

kmp匹配的模板题吧。

#include 

using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
int s[maxn],t[maxm];
int nxt[maxm];
void getnxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(t[temp+1]==t[i]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1){
            if(t[i]==t[0]){nxt[i]=0;}
        }
    }
   // for(int i=0;i
}

int f(){
    int j=0;
    for(int i=0;i<=n;i++){
        while(j&&s[i]!=t[j]){
            j=nxt[j-1]+1;
        }
        if(s[i]==t[j]){
            j++;
        }
        if(j==m)return i-j+2;
    }
    return -1;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=0;iscanf("%d",&s[i]);
        }
        for(int i=0;iscanf("%d",&t[i]);
        }
        getnxt();
        printf("%d\n",f());
    }
    return 0;
}

B - Oulipo HDU - 1686

同模板题,不过要输出匹配上的次数。

#include 
#include

using namespace std;
typedef long long LL;
const int maxn=1000000;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char a[maxn],b[maxn];
int ans,alen,blen;
int nxt[maxn];

void getnxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(b[i]==b[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1)if(b[i]==b[0])nxt[i]=0;
    }
}

void f(){
    int j=0;
    for(int i=0;iwhile(j&&a[i]!=b[j])j=nxt[j-1]+1;
        if(a[i]==b[j])j++;
        if(j==blen){
            ans++;
        }
    }
}

int main()
{
        scanf("%d",&n);
        while(n--){
        scanf("%s%s",b,a);
        ans=0;
        alen=strlen(a);
        blen=strlen(b);
        getnxt();
        f();
        printf("%d\n",ans);
    }
    return 0;
}

C - 剪花布条 HDU - 2087

还是字符串匹配,不过匹配出来的串不能互相重叠,所以每次匹配成功后要把j设为0.

#include 

using namespace std;
typedef long long LL;
const int maxn=1005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char a[maxn],b[maxn];
int ans,alen,blen;
int nxt[maxn];

void getnxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(b[i]==b[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1)if(b[i]==b[0])nxt[i]=0;
    }
}

void f(){
    int j=0;
    for(int i=0;iwhile(j&&a[i]!=b[j])j=nxt[j-1]+1;
        if(a[i]==b[j])j++;
        if(j==blen){
            ans++;
            j=0;
        }
    }
}

int main()
{
    while(scanf("%s",a)!=EOF){
        if(a[0]=='#')break;
        scanf("%s",b);
        ans=0;
        alen=strlen(a);
        blen=strlen(b);
        getnxt();
        f();
        printf("%d\n",ans);
    }
    return 0;
}

D - Cyclic Nacklace HDU - 3746

项链要求颜色排列为大于1的循环,让你往项链的左右添加珠子使得满足条件,输出最小添加数量。
需要利用next指针的性质找出字符串的循环节,如果可以被字符串长度整除的话就已经满足了,否则添加珠子使满足被整除。

#include 
using namespace std;
typedef long long LL;
const int maxn=100005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];

void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1){
            if(str[i]==str[0]){nxt[i]=0;}
        }
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",str);
        len=strlen(str);
        get_nxt();
        int ans;
        int cnt=len-nxt[len-1]-1;
        if(nxt[len-1]+1==0){ans=len;}
        else if(len%cnt==0)ans=0;
        else{
            ans=len%cnt;
            ans=cnt-ans;
        }
        printf("%d\n",ans);
    }
    return 0;
}

E - Period HDU - 1358

A^K表示K个字符串A连接起来,让你找出所有前缀可以那样表示且K>1的。同一个前缀只找到k最大的那个。

还是利用next指针的特性找可以前缀长度被整除的循环节即可。

#include 
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];

void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1){
            if(str[i]==str[0]){nxt[i]=0;}
        }
    }
    for(int i=0;iint main()
{
    int cas=0;
    while(~scanf("%d",&len),len){
        cas++;
        printf("Test case #%d\n",cas);
        scanf("%s",str);
        get_nxt();
        for(int i=0;iif((i+1)%(i+1-nxt[i])==0){
                if((i+1)/(i+1-nxt[i])>1)
                printf("%d %d\n",i+1,(i+1)/(i+1-nxt[i]));
            }
        }
        printf("\n");
    }
    return 0;
}

F - The Minimum Length HUST - 1010

还是找最小循环节。

#include 
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];

void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1){
            if(str[i]==str[0]){nxt[i]=0;}
        }
    }
    for(int i=0;iint main()
{

    while(~scanf("%s",str)){
        len=strlen(str);
        get_nxt();
        int ans=len-nxt[len-1];
        printf("%d\n",ans);
    }
    return 0;
}

G - Power Strings POJ - 2406

找最大的n使得存在a^n=s,
同样是找满足整除关系的最小循环节。

//#include 
#include
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];

void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1){
            if(str[i]==str[0]){nxt[i]=0;}
        }
    }
    for(int i=0;iint main()
{

    while(~scanf("%s",str),str[0]!='.'){
        len=strlen(str);
        get_nxt();
        int ans;
        int cnt=len-nxt[len-1];
        if(cnt==0)ans=1;
        else {
            if(len%cnt==0)
            ans=len/cnt;
            else ans=1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

H - Seek the Name, Seek the Fame POJ - 2752

找同时是字符串前缀和后缀的子串。

沿着最后一个字母的next字母往回走的那些就是满足条件的了

//#include 
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];
vector<int> ans;
void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1){
            if(str[i]==str[0]){nxt[i]=0;}
        }
    }
}

int main()
{
    while(~scanf("%s",str)){
        ans.clear();
        len=strlen(str);
        int len1=len;
        get_nxt();
        ans.push_back(len);
        len--;
        while(len>0){
            len=nxt[len];
            if(len<=0)break;
            ans.push_back(len+1);
        }
        if(str[0]==str[len1-1]&&len1!=1)ans.push_back(1);
        sort(ans.begin(),ans.end());
        int sss=ans.size();
        for(int i=0;iprintf("%d",ans[i]);
            if(i1)printf(" ");
        }
        puts("");
    }
    return 0;
}

I - Blue Jeans POJ - 3080

多字符串的最长公共子串。
数据量小,暴力匹配了。。。

//#include 
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
string str[15];
int len;
int nxt[maxn];
void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1){
            if(str[i]==str[0]){nxt[i]=0;}
        }
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;icin>>str[i];
        }
        len=str[0].length();
        string ans;
        bool f=0;
        for(int i=len;i>=3;i--){
            for(int j=0;j+i-1string cnt=str[0].substr(j,i);
                bool f1=1;
                for(int k=1;kint cur=str[k].find(cnt);
                    if(cur==-1)f1=0;
                }
                if(f1){
                    if(cnt"")
                    ans=cnt;
                    f=1;
                }
            }
            if(f)break;
        }
        if(f==0)printf("no significant commonalities\n");
        else{cout<return 0;
}

J - Simpsons’ Hidden Talents HDU - 2594

找a串的前缀和b串的后缀最长的相等部分。
a串求next指针去匹配b串,匹配到结尾时a串的匹配长度就是答案。
我的匹配的部分似乎有点问题???

//#include 
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int maxn=50005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char a[maxn],b[maxn];
int nxt[maxn];
int lena,lenb;

void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint temp=nxt[i-1];
        while(temp!=-1){
            if(a[i]==a[temp+1]){nxt[i]=temp+1;break;}
            temp=nxt[temp];
        }
        if(temp==-1){
            if(a[0]==a[i])nxt[i]=0;
        }
    }
    for(int i=0;iint kmp(){
    int j=0;
    for(int i=0;iwhile(j!=0&&a[j]!=b[i]){
            j=nxt[j-1];
        }
        if(a[j]==b[i]){j++;}
        if(j==lena&&i!=lenb-1)j=nxt[j-1];
    }
    return j;
}

int main()
{
    while(~scanf("%s%s",a,b)){
        lena=strlen(a);
        lenb=strlen(b);
        get_nxt();
        int ans=kmp();
        for(int i=0;iputchar(a[i]);
        if(ans)putchar(' ');
        printf("%d\n",ans);
    }
    return 0;
}

K - Count the string HDU - 3336

求所有前缀在字符串中匹配成功次数之和。
还是利用next求最长公共前后缀的性质,从每个位置沿着next指针往回不断对答案+1就行了。

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a));
typedef long long ll;
const int maxn=200005;
const int maxm=1005;
const int inf=0x3f3f3f3f;
const int mod=10007;
int  n,m;
char s[maxn];
int nxt[maxn];
int num[maxn];//当前这个前缀的相同串有几个
void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint tmp=nxt[i-1];
        while(tmp!=-1){
            if(s[i]==s[tmp+1]){nxt[i]=tmp+1;break;}
            tmp=nxt[tmp];
        }
        if(tmp==-1){
            if(s[i]==s[0])nxt[i]=0;
        }
    }

}


int main()
{
   int T;
   scanf("%d",&T);
   while(T--){
    scanf("%d",&n);
    scanf("%s",s);
   get_nxt();
   int ans=0;
    for(int i=n-1;i>=0;i--){
       int j=i;
       while(j!=-1){
            ans=(ans+1)%mod;
            j=nxt[j];
       }
    }
    printf("%d\n",ans);
   }
   return 0;
}

L - Clairewd’s message HDU - 4300

一串密文与明文前面一部分(可能完整)连接起来的文本,让你输出完整的最短密文明文。

明文那部分翻译成密文,然后求next指针,就能的到最长的公共前后缀,那个就是明文的长度了。

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a));
typedef long long ll;
const int maxn=100005;
const int maxm=1005;
const int inf=0x3f3f3f3f;
const int mod=10007;
int  n,m;
char ff[30],ff1[30];
char s[maxn],t[maxn];
int nxt[maxn];

void get_nxt(){
    memset(nxt,-1,sizeof(nxt));
    for(int i=(n-1)/2+1;iint tmp=nxt[i-1];
        while(tmp!=-1){
            if(s[i]==s[tmp+1]){nxt[i]=tmp+1;break;}
            tmp=nxt[tmp];
        }
        if(tmp==-1){
            if(s[i]==s[0])nxt[i]=0;
        }
    }
}

int main()
{
    int T;
   scanf("%d",&T);
   while(T--){
    scanf("%s%s",ff,t);
    n=strlen(t);
    for(int i=0;i<26;i++){
        ff1[ff[i]-'a']=i+'a';
    }
    for(int i=0;ifor(int i=(n-1)/2+1;i'a'];
    }
   get_nxt();
   int ans=nxt[n-1]+1;
   ans=n-ans;
   for(int i=0;iputchar(t[i]);
   }
   for(int i=0;iputchar(ff1[t[i]-'a']);
   }
   puts("");
   }
   return 0;
}

M - Substrings HDU - 1238

还是暴力匹配了,数据应该不科学

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a));
typedef long long ll;
const int maxn=100005;
const int maxm=1005;
const int inf=0x3f3f3f3f;
const int mod=10007;
int  n,m;
string str[105];
int main()
{
  int T;
  scanf("%d",&T);
  while(T--){
    scanf("%d",&n);
    int id=0;
    for(int i=0;icin>>str[i];
        if(str[i].length()int ans=0;
    int len=str[id].length();
    for(int lll=len;lll>0;lll--){
        for(int i=0;i+lll-1string cnt=str[id].substr(i,lll);
            string cnt1=cnt;
            reverse(cnt1.begin(),cnt1.end());
            bool f=1;
            for(int j=0;jif(j==id)continue;
                if(str[j].find(cnt)==string::npos&&str[j].find(cnt1)==string::npos){f=0;break;}
            }
            if(f){ans=lll;break;}
        }
        if(ans)break;
    }
    printf("%d\n",ans);
  }
   return 0;
}

Z - Theme Section HDU - 4763

找字符串前缀中间后缀三段相同不重叠的最长子串。
我这个参考别人写的二分应该是错的,虽然过了。
正解应该是找最长公共前后缀,然后kmp中间部分看能不能匹配上,不能的话next指针再往前走一步,直到能匹配上,这样才能保证前后缀始终相等。


#include
#include
#include
#include
#include
#include
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a));
typedef long long ll;
const int maxn=1000005;
const int maxm=3000005;
const int inf=0x3f3f3f3f;
int n,len;
int nxt[maxn];
char str[maxn];

void get_nxt(char *s){
    memset(nxt,-1,sizeof(nxt));
    for(int i=1;iint tmp=nxt[i-1];
        while(tmp!=-1){
            if(s[i]==s[tmp+1]){nxt[i]=tmp+1;break;}
            tmp=nxt[tmp];
        }
        if(tmp==-1){
            if(s[i]==s[0])nxt[i]=0;
        }
    }
}

bool kmp(int k){
    int i=k-1,j=0;
    while(iif(j==-1||str[i]==str[j]){i++;j++;}
        else j=nxt[j];
    }
    return j>=k;
}


int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",str);
        n=strlen(str);
        get_nxt(str);
        int hh=min(n/3,nxt[n-1]+1);
        int lef=0,rig=hh;
        int ans=0;
        while(lef<=rig){
            int mid=(lef+rig)/2;
            if(kmp(mid)){ans=mid;lef=mid+1;}
            else rig=mid-1;
        }
        printf("%d\n",ans);
    }
   return 0;
}

你可能感兴趣的:(字符串)