HDU 4300 Clairewd's message ( 拓展KMP )
题意 : 给你一个包含26个小写字母的明文密文转换信息字符串str,第一个表示'a'对应的密文是str[0]、'b'对应str[1]……以此类推。接下来一行给你一个另一个字符串,这个字符串由密文+明文组成,但是现在后面部分的明问可能有不完整的情况(也有可能缺失只包含密文),问你现在最少需要补充多多少个字符串才能使得字符串变成完整的满足==>密文+密文对应的明文 组成的字符串,将这个完整的字符串输出出来。
分析 : 冷静分析一下可以发现,在给出的残缺字符串中,前面的一半肯定是属于密文的!如果不是这样,那明文就会比密文长了,这是不可能的。然后可以发现,后半段的各个后缀,只要找出一个最长的后缀使得其和前面半段的密文对应的明文相匹配,那么我们需要补充的字符串就是最少需要补充的!举个栗子来说就是,比如假设对照表的明文和密文是一样的,也就是'a'就是'a',‘b'就是’b',对于abcdab,先截取前半段 abc 翻译一下得到的明文还是 abc,现在考虑后半段 dab ,现在最长的后缀 dab 是无法与翻译过后的明文相匹配,所以跳过接下来考虑第二长的 ab 这个后缀,发现匹配了因此 ab 即为后半段明文的开始前缀,而我们需要补充的就只有 cd 这个字符串的明文,故答案是 abcdabcd。发现为了完成这个任务,拓展KMP相当合适,只要将前半段作为模板串,后半段作为主串跑一遍拓展KMP,得到extend数组,只要现在满足 i + extend[i] == len 即满足要求,具体看代码。
#include<string.h> #include<stdio.h> #include<map> using namespace std; const int maxn = 1e5 + 10; map<char, char> mp; char str[maxn], S[maxn], mo[maxn]; int Next[maxn], extend[maxn], moL, strL, len, Hlen; void GetNext() { Next[0] = moL; int a, p; for (int i = 1, j = -1; i < moL; i++, j--){ if (j < 0 || i + Next[i - a] >= p){ if (j < 0) p = i, j = 0; while (p < moL && mo[p] == mo[j]) p++, j++; Next[i] = j; a = i; } else Next[i] = Next[i - a]; } } void GetExtend() { GetNext(); int a, p; for (int i = 0, j = -1; i < strL; i++, j--){ if (j < 0 || i + Next[i - a] >= p){ if (j < 0) p = i, j = 0; while (p < strL && j < moL && S[p] == mo[j]) p++, j++; extend[i] = j; a = i; } else extend[i] = Next[i - a]; } } inline void PrintAns() { GetExtend(); int index = -1; for(int i=0,j=Hlen; i<strL; j++,i++){ if(j + extend[i] == len){///找最长的前后缀使得其与前半段匹配 index = j; break; } } if(index == -1) index = len;///如果没有这样的后缀,那么说明原来本身就不包含明文,需要全部补充上去 bool flag = false; for(int t=1; t<=2; t++){ if(!flag){ for(int i=0; i<index; i++) putchar(str[i]); flag = true; }else{ for(int i=0; i<index; i++) putchar(mp[str[i]]); } }puts(""); } int main(void) { int nCase; scanf("%d", &nCase); while(nCase--){ mp.clear(); char tmp; for(int i=0; i<26; i++){ scanf(" %c", &tmp); mp[tmp] = 'a'+i;///用map构建明文——密文对照表 } scanf("%s", str); len = strlen(str); Hlen = (len+1)/2; moL = Hlen, strL = len - Hlen; for(int i=0; i<Hlen; i++) mo[i] = mp[str[i]];///截取前半段的“译文”作为模板串 for(int j=0,i=Hlen; i<len; j++,i++) S[j] = str[i];///截取后半段作为母串 PrintAns(); } return 0; }