Clairewd’s message ekmp

  给两个串第一个串是翻译表(密文可以通过翻译表翻译成明文),第二个串是由密文+明文组成,前面是密文(完整的),后面是明文(未必完整),问能不能把第二个串补全,输出最短的一种可能。

一开始 用的string   和每次更新字符进行getnext  

妥妥的 超时   kmp最好不要用string  速度太慢了

参考了 kuangbin的做法:

以s为原串做一次扩展KMP,得到extend数组,extend[i]表示原串以第i开始与模式串的前缀的最长匹配。经过O(n)的枚举,我们可以得到,若extend[i]+i=len且i>=extend[i]时,表示stringB即为该点之前的串,stringA即为该点之前的str串,最后输出即可。

就是原串和翻译后的串进行一次ekmp  然后进行判断输出即可

#include<bits/stdc++.h>
using namespace std;
//input by bxd
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define RI(n) scanf("%d",&(n))
#define RII(n,m) scanf("%d%d",&n,&m)
#define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define RS(s) scanf("%s",s);
#define LL long long
#define REP(i,N)  for(int i=0;i<(N);i++)
#define CLR(A,v)  memset(A,v,sizeof A)
//////////////////////////////////
#define inf 0x3f3f3f3f
#define N 100000+50
int nex[N];
int extend[N];
char p[N];
char s[N];
char table[N];
map<char,char>mp;
void EKMP(char s[],char t[])//s[]为主串,t[]为模式串
{
    int i,j,p,l;
    int len=strlen(t);
    int len1=strlen(s);
    memset(nex,0,sizeof(nex));
    memset(extend,0,sizeof(extend));
    nex[0]=len;
    j=0;
    while(j+1<len&&t[j]==t[1+j])j++;
    nex[1]=j;
    int a=1;
    for(int i=2;i<len;i++)
    {
        p=nex[a]+a-1;
        l=nex[i-a];
        if(i+l<p+1)nex[i]=l;
        else
        {
            j=max(0,p-i+1);
            while(i+j<len&&t[i+j]==t[0+j])j++;
            nex[i]=j;
            a=i;
        }
    }
    j=0;
    while(j<len1&&j<len&&s[j]==t[j])j++;
    extend[0]=j;
    a=0;
    for(i=1;i<len1;i++)
    {
        p=extend[a]+a-1;
        l=nex[i-a];
        if(l+i<p+1)nex[i]=l;
        else
        {
            j=max(0,p-i+1);
            while(i+j<len1&&j<len&&s[i+j]==t[j])j++;
            extend[i]=j;
            a=i;
        }
    }
}

int main()
{
    int cas;
    RI(cas);
    while(cas--)
    {
        RS(table);
        RS(s);
        rep(i,0,25)
        mp[table[i]]='a'+i;
        int len=strlen(s);
        rep(i,0,len-1)
        p[i]=mp[ s[i] ];
        EKMP(s,p);
        int k;
        for(k=(len+1)/2;k<len;k++)
        {
            if(extend[k]+k>=len)//+k 意为当前坐标之前有几个字母 直接为k即可
            break;
        }
        rep(i,0,k-1)
        printf("%c",s[i]);
        rep(i,0,k-1)
        printf("%c",mp[s[i]]);
        cout<<endl;
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/bxd123/p/10685781.html