Clairewd’s message
这道题巧在思想!!!!
题目意思为密文和明文连在一块了,让你还原····
看题目实在无从下手
最开始想的办法是暴力!!!!从len/2开始暴力
假设前半部分为密文,后边剩的为明文,直接暴力
但是还有个问题,暴力出来后,如何把明文补全?
以前有道题求最短回文字符串,但是那道题中回文字符串的循环次数没有限制
比如 abcabca
会输出 abcabcabc 这样就是最短回文字符串
但是这道题会输出 abcabcabcabc
这才是符合题意的输出
简单来说,就是回文字符串的循环次数必须为2
那么这样就真的不好做了
虽然kmp和Ekmp也能做,但是好像也不是那么快,那么顺利
但是,换个思路这道题就很简单了
上面的思路都是要直接求出来最终结果!!
就是直接把串求出来(密文+明文)
但是如果我直接求密文,然后密文输出一次,然后翻译成明文再输出一下就OK了
emmmm,但是仔细一想,直接求密文好像也不简单啊!!!
那么我求明文!!!,明文找到后,前面就为密文了!!!!
此时新方法就应运而生!!!!
将字符串一分为2,然后后半部分当成明文(虽然不一定全部是明文,但是明文一定都在后半段)
将前面的密文转换为明文,这样统一一下,方便后面用
然后把后半段当成母串,前半段当成子串(注意顺序一定不要弄反!!!)
进行KMP匹配!!
最后看能匹配上几个,那么表示明文有多少
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<map>
using namespace std;
map <char,char> cod; //表示密文所对应的原文
char s2[30]; //译表
char s1[100005],p[50005],s[50005];
int len,len1,len2,nextl[50005];
//s1是原串,s1代表截获串,p1表示自己假设的最短密文,s表示假设最长的明文
void getnext()
{
int i,j;
nextl[0]=0,nextl[1]=0;
for(i=1;i<len1;i++)
{
j=nextl[i];
while(j&&p[i]==p[j])
j=nextl[j];
if(p[i]==p[j])
nextl[i+1]=j+1;
else
nextl[i+1]=0;
}
}
int KMP()
{
int i,j=0;
for(i=0;i<len2;i++)
{
while(j&&s[i]!=p[j])
j=nextl[j];
if(s[i]==p[j])
j++;
}
return j;
}
int main()
{
int T,i;
scanf("%d",&T);
while(T--)
{
scanf("%s",s2);
for(i=0;i<26;i++)
cod[s2[i]]='a'+i;
scanf("%s",s1);
len=strlen(s1);
len1=len/2;
if(len&1)
len1++;//len1表示密文长度
len2=len-len1;
for(i=0;i<len1;i++)//把密文单独拉出来
p[i]=cod[s1[i]]; //把密文转化为明文方便匹配
p[len1]='\0'; //把p搞定(字符串的末尾为\0)
//cout<<p<<endl;
for(i=0;i<len2;i++)//把明文单独拉出来
s[i]=s1[len1+i];
s[len2]='\0';
getnext();
int s=KMP();
int flag=len-s*2; //标记还有多少木有匹配的
printf("%s",s1); //原串必须会输出的
for(i=s;i<s+flag;i++)
printf("%c",cod[s1[i]]);
puts("");
}
return 0;
}