Description
Input
Output
Sample Input
2 abcdefghijklmnopqrstuvwxyz abcdab qwertyuiopasdfghjklzxcvbnm qwertabcde
Sample Output
abcdabcd qwertabcde
Hint
Range of test data: T<= 100 ; n<= 100000;
这道题有一个很操蛋的地方,就是你读一遍之后根本不知道它在说什么。鉴于智商问题,我读了七八遍之后仍然不能理解,最终百度的,不过,其中GFW的梗还挺好笑。。。
题目大意:给出一个加密字典,即一个字母映射为另一个字母,类似于函数关系;给出一个字符串,它是由 完整的加密串+不一定完整的解密串 相连而成,要求将这个串补充完整,即将不完整的部分补充完整,并且这个完整的串还是最短的。
解题思路:1. 将原始串再做一次加密,加密+解密 就会变成 XX+加密
2. 设原始串为s(主串),新串为t(模式串),对两串进行扩展KMP运算
3. 从中间开始扫描extend数组,如果遇到i+extend[i]==len&&i>=extend[i]的情况,表明找到解。
解释:为了找到最短的串,我们要尽量让加密串与解密串一样长,也就是要从中间开始找,如果找到一个解,那么从i到len的串必定全部匹配,i既表示解密串的起始位置,也表示加密串的长度,extend[i]则表示后一半长度,这就是i+extend[i]==len,而且,要保证两部分不重合,这就是i>=extend[i]。
代码
#include <iostream> #include <stdio.h> #include <string.h> #define MAX 100010 using namespace std; void get_next(char x[],int m,int next_[]) { next_[0]=m; int j=0; while(j+1<m&&x[j]==x[j+1]) j++; next_[1]=j; int k=1; for(int i=2; i<m; i++) { int p=next_[k]+k-1; int L=next_[i-k]; if(i+L<p+1) next_[i]=L; else { j=max(0,p-i+1); while(i+j<m&&x[i+j]==x[j]) j++; next_[i]=j; k=i; } } } void ekmp(char x[],int m,char y[],int n,int next_[],int extend[]) { get_next(x,m,next_); 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=next_[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 s[MAX],t[MAX],dic[27]; char dc[200]; int len; int Next[MAX],extend[MAX]; int main() { int T,i,j,bg; char tmp; scanf("%d",&T); while(T--) { scanf("%s%s",dic,t); len=strlen(t); for(i=0;dic[i]!='\0';i++)//计算解密字典 { dc[dic[i]-'a']='a'+i; } for(i=0; i<len; i++)//二次加密 { s[i]=dic[t[i]-'a']; } ekmp(t,len,s,len,Next,extend); for(i=len/2; i<len; i++) { if(i+extend[i]==len && i>=extend[i])//重要,需要长度相等且不重合 break; } for(j=0; j<i; j++) printf("%c",t[j]); for(j=0; j<i; j++)//输出解密内容 printf("%c",dc[t[j]-'a']); printf("\n"); } return 0; }