题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4300
解题思路:
题意描述挺难理解。
大致意思是这个人截到某人发的秘文,如果发完是加密的发一遍紧接着发不加密的
现在有三种情况:
1.加密的还没发完
2.加密发完,不加密发了一部分
3.都刚好发完
最后总结为这一段字符最多一半是不加密的。
那么我们要找和前文加密的匹配部分的话先把这一段字符串的前一半翻译成不加密的,然后对整个段求KMP,得到小于一半的最长相同前后缀并由此判断出原文长度。并由此将未加密的部分补完。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
using namespace std;
const int N = 1e5+5;
char s[N],trans[30];
int mp[30];///mp[i]翻译后为(i+96)的字母转换为真正的字母的ASCII码
int fail[N];
int KMP(int len)///返回循环节大小
fail[0] = -1;
for (int i=0,j=-1;i<len;){
if (j==-1||s[i]==s[j]){
i++,j++;
fail[i] = j;
}
else j = fail[j];
}
int now = len;
while (2*fail[now]>len){
now = fail[now];
}
return len - fail[now];
}
int main()
{
//freopen("C:\\Users\\DELL\\Desktop\\input.txt", "r", stdin);
int T;
scanf("%d",&T);
while (T--){
scanf("%s",trans);
for0(i,0,26) mp[trans[i]-96] = i+97;
scanf("%s",s);
int len = strlen(s);
for0(i,0,(len+1)/2) s[i] = mp[s[i]-96];
//puts(s);
int l = KMP(len);
//printf("l=%d\n",l);
for0(i,0,(len+1)/2) printf("%c",trans[s[i]-97]);///翻译成原文的部分翻译回来
if (len%2==0 && l == len/2){
for0(i,len/2,len) printf("%c",s[i]);///正好剩下一半全部为原文,直接翻译
}
else {
for0(i,(len+1)/2,len) printf("%c",s[i]); /// (len+1)/2 ~ len-1 原文照常输出
for0(i,len-l,(len+1)/2) printf("%c",s[i]);/// len-l 为需要补足的部分的起点,这一部分翻译为了原文,可以直接使用
for0(i,(len+1)/2,l) printf("%c",mp[s[i]-96]);///剩下部分为剩余未翻译的需要补足的部分
}
printf("\n");
}
return 0;
}