题目:http://acm.hdu.edu.cn/showproblem.php?pid=4300
题意:有一段字符串由密文和明文组成,密文是完好的,明文可能是残缺的。首先给出密文转换表,然后给出密文明文字符串,然后复原出最短的密文明文字符串(补上明文)
思路:可以用扩展kmp求解。首先,明文的起始位置一定在字符串后半段(密文 >= 明文),于是把前半段转换成明文,然后用扩展kmp求每一个位置和字符串的最长公共前缀长度,后半段中满足next[i] + i = len的i,一定是明文的起始位置,因为这代表从i开始以后完全重合与字符串首部的一段字符。注意明文可能完全丢失
扩展kmp
#include <bits/stdc++.h>
using namespace std;
const int N = 100000 + 10;
char s1[30], s2[N], s3[N], table[30];
int Next[N];
void get_next(char *pat)
{
int len = strlen(pat);
Next[0] = len;
int k = 0;
while(k + 1 < len && pat[k] == pat[k+1]) ++k;
Next[1] = k;
k = 1;
for(int i = 2; pat[i]; i++)
{
if(i + Next[i-k] - 1 < k + Next[k] - 1) Next[i] = Next[i-k];
else
{
int j = k + Next[k] - i;
if(j < 0) j = 0;
while(i + j < len && pat[j] == pat[i+j]) ++j;
Next[i] = j;
k = i;
}
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%s%s", s1, s2);
for(int i = 0; i < 26; i++) table[s1[i]-'a'] = i + 'a';//密文字符对应的明文字符
int len = strlen(s2);
for(int i = 0; i < (len+1)/2; i++) s3[i] = table[s2[i]-'a'];
for(int i = (len+1)/2; i <= len; i++) s3[i] = s2[i];
get_next(s3);
int ans = len;//当找不到公共前缀时,意味整个串都是密文,所以预先给res赋值为len
for(int i = (len+1)/2; i < len; i++)
if(i + Next[i] == len)//条件成立说明从i位置开始到末尾是字符串的前缀,认为从i开始是明文
{
ans = i; break;
}
printf("%s", s2);
for(int i = len-ans; i < ans; i++) printf("%c", table[s2[i]-'a']);
printf("\n");
}
return 0;
}
kmp做法。思路也是求后半段中和字符串的最长公共前缀长度
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <map>
#include <vector>
#include <cstring>
#include <cmath>
#include <set>
using namespace std;
const int N = 100100;
char s1[100], tab[100], s2[N], s3[N];
int Next[N];
void get_next(char *pat)
{
int i = 0, j = -1;
Next[0] = -1;
while(pat[i])
{
if(j == -1 || pat[i] == pat[j])
{
++i, ++j;
if(pat[i] != pat[j]) Next[i] = j;
else Next[i] = Next[j];
}
else j = Next[j];
}
}
int kmp(char *ori, char *pat)
{
get_next(pat);
int i = (strlen(ori)+1) / 2, j = 0;
while(ori[i])
{
if(j == -1 || ori[i] == pat[j]) ++i, ++j;
else j = Next[j];
}
return j;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%s%s", s1, s2);
int len = strlen(s2);
for(int i = 0; i < 26; i++)
tab[s1[i]-'a'] = i + 'a';
for(int i = 0; i < (len + 1) / 2; i++)
s3[i] = tab[s2[i]-'a'];
for(int i = (len + 1) / 2; i <= len; i++)
s3[i] = s2[i];
int res = kmp(s2, s3);
printf("%s", s2);
for(int i = res; i < len - res; i++)
printf("%c", tab[s2[i]-'a']);
printf("\n");
}
return 0;
}