题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4300
题目大意:第一个字符串A是密码表,为a~z所对应的新字母,第二个字符串B是密码+(部分)原文构成的字符串,现在让你补全这个字符串,使得这个字符串前半部分是密码,后半部分是原文
思路: 先将串B全部看成是密码,然后利用密码表将其转换成一个新串C,所以,原文就出现在了串B的后缀,和串C的前缀这两个地方。利用扩展Kmp中的extend数组,求出B的后缀与串C的最长公共前缀即可
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
char s[28], str[maxn], c[maxn];
int kmp_next[maxn], extend[maxn];
map<char, char> pp;
void pre_kmp(char x[], int m, int kmp_next[])
{
kmp_next[0] = m;
int j = 0, k = 1;
while(j+1 < m && x[j] == x[j+1]) j++;
kmp_next[1] = j;
for(int i = 2; i < m; i++){
int p = kmp_next[k] + k -1;
int l = kmp_next[i-k];
if(i+l < p + 1) kmp_next[i] = l;
else{
j = max(0, p-i+1);
while(i + j < m && x[i+j] == x[j]) j++;
kmp_next[i] = j;
k = i;
}
}
}
void exkmp(char x[], int m, char y[], int n, int kmp_next[], int extend[])
{
pre_kmp(x, m, kmp_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 = kmp_next[i-k];
if(i+l < p+1) extend[i] = l;
else{
j = max(0, p-i+1);
while(i+j < n && y[i+j] == x[j] && j < m) j++;
extend[i] = j;
k = i;
}
}
}
int main()
{
int T;
cin >> T;
while(T--){
cin >> s >> str;
int len = strlen(str);
int len2 = strlen(s);
for(int i = 0 ; i < len2; i++){
pp[s[i]] = 'a'+i;
}
for(int i = 0; i < len; i++){
c[i] = pp[str[i]];
}
c[len] = 0;
exkmp(c, len, str, len, kmp_next, extend);
int k;
for(k = 0; k < len; k++){
if(k + extend[k] >= len && k >= extend[k]) break;
}
for(int i = 0; i < k; i++) printf("%c", str[i]);
for(int i = 0; i < k; i++) printf("%c", pp[str[i]]);
cout << endl;
}
return 0;
}