题意:给你两个字符串 key 和 S,key 代表密钥,可以把明文转化成密文, S 是一串完整的密文加上这段密文对应的明文(明文部分后面可能有缺失),求输出完整的密文+明文,(如果根据字符串 S 有多种答案,输出最短的答案)。
分析:我们把 S 当作全明文利用 key 转化为 字符串 T,则 S 中后部分的明文就转化成了 T 中对应的密文,则问题就转化为求 T 的后缀(母串) 和 S 的前缀(子串)的相等子串问题,即扩展KMP。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 1e5+10;
int n;
int extend[N],nxt[N];
char key[30],ace[30];
char S[N];
char T[N];
void ExGetNext(char s[])
{
int i=0,j,po;
nxt[0]=n;
while(i+1<n&&s[i]==s[i+1]) i++;
nxt[1]=i;
po=1;
for(i=2;i<n;i++)
{
if(nxt[i-po]+i<nxt[po]+po)
nxt[i]=nxt[i-po];
else
{
j=nxt[po]+po-i;
if(j<0) j=0;
while(i+j<n&&s[j]==s[j+i]) j++;
nxt[i]=j;
po=i;
}
}
}
void ExKmp(char a[],char b[])
{
int i=0,j,po,l1=n,l2=n;
while(i<l2&&i<l1&&a[i]==b[i]) i++;
extend[0]=i;
po=0;
for(i=1;i<l1;i++)
{
if(nxt[i-po]+i<extend[po]+po)
extend[i]=nxt[i-po];
else
{
j=extend[po]+po-i;
if(j<0) j=0;
while(i+j<l1&&j<l2&&a[j+i]==b[j]) j++;
extend[i]=j;
po=i;
}
}
}
int main()
{
freopen("1.txt","r",stdin);
int tt;
scanf("%d",&tt);
while(tt--)
{
scanf("%s%s",key,S);
n=strlen(S);
for(int i=0;i<26;i++)
{
ace[key[i]-'a']=i; //反key,密文转明文用
}
for(int i=0;i<n;i++)
T[i]=key[S[i]-'a'];
ExGetNext(S);
ExKmp(T,S);
int t;
for(t=(n+1)/2;t<n;t++)
{
if(t+extend[t]>=n) break; //根据题意,S中密文长度肯定大于等于明文
}
for(int i=0;i<t;i++)
putchar(S[i]);
for(int i=0;i<t;i++)
putchar(ace[S[i]-'a']+'a');
puts("");
}
return 0;
}