题目大意:
给出一串S,代表加密字符,表示第i个字符会加密为S[i]
再给出一段密文,明文相连接的字符串,密文是完整的,明文不一定完整,要我们填补明文,使得密文+明文相连的字符串长度尽可能小。
解题思路:
不妨先把给出的密文+明文的字符串加密操作,那么后缀的一段肯定是明文(或者0),与原本的串匹配,公共前后缀长度不超过 len/2的情况下,能匹配的越长,就能越少地填充回去。exkmp处理即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
/*
* 扩展KMP算法
*/
//nxt[i]:x[i...m-1]与x[0...m-1]的最长公共前缀
//extend[i]:y[i...n-1]与x[0...m-1]的最长公共前缀
int nxt[100050];
int extend[100050];
char key[100050], t[100050],s[100050];
void pre_EKMP(char x[],int m,int nxt[])
{
nxt[0]=m;
int j=0;
while(j+1<m && x[j]==x[j+1]) j++;
nxt[1]=j;
int k=1;
for(int i=2;i<m;i++)
{
int p=nxt[k]+k-1;
int L=nxt[i-k];
if(i+L<p+1)nxt[i]=L;
else
{
j=max(0,p-i+1);
while(i+j<m && x[i+j]==x[j])j++;
nxt[i]=j;
k=i;
}
}
}
void EKMP(char x[],int m,char y[],int n,int nxt[],int extend[])
{
pre_EKMP(x,m,nxt);
int j=0;
while(j<n && j<m && x[j]==y[j]) j++;
extend[0]=j;
int k=0;
for(int i=1;i<n;i++)
{
int p=extend[k]+k-1;
int L=nxt[i-k];
if(i+L<p+1)
extend[i]=L;
else
{
j=max(0,p-i+1);
while(i+j<n && j<m && y[i+j]==x[j]) j++;
extend[i]=j;
k=i;
}
}
}
char rnm[30];
int main()
{ int tt;
cin>>tt;
while(tt--){
cin >> key >> t; // 密码串
int plen=strlen(key);
int tlen = strlen(t);
int slen=tlen;
memset(rnm,'\0',sizeof(rnm));
for(int i=0;i<26;i++){
rnm[key[i]-'a']=i+'a';
}
for(int i=0;i<tlen;i++){
s[i]=key[t[i]-'a']; // 解密
}
EKMP(t,tlen,s,slen,nxt,extend);
int ans =tlen;
for(int i=0; i <tlen; i++)
{
if(extend[i]+i>=tlen&&i>=extend[i])
{
ans=i;
break;
}
}
for(int i=0;i<ans;i++){
printf("%c",t[i]);
}
printf("%c",rnm[t[i]-'a']);
}
printf("\n");
}
return 0;
}