超级传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4300
本题题意是给你一个字符对应表,再给一个密文和明文相连接的串(明文后缀可能缺失),求补全后的串。
比如样例:
abcdefghijklmnopqrstuvwxyz
abcdab
为了方便,字符对应表是默认的。abcdab便是密文和明文相连接的串,记长度为m,我们需要找到分割点。
利用扩展KMP将该串所有后缀与该串前缀进行匹配,如果能匹配到最后一个字符,即k+P[k] == m,则该处可进行分割,找到最小的k值,即可进行最小分割。还要注意,k位置必须在串的后一半,即串中密文字符数不能少于明文字符数。匹配的时候前缀部分要根据字符转换表进行转换(即将前缀看成密文,将其转换为明文,再与后缀部分明文进行匹配)。
代码如下:
#include <cstdio>
#include <cstring>
using namespace std;
int P[100010];
char table[26], rev_table[26];
char B[100010];
int ex_preprocess(char* B, int* P, int m = -1)
{
int a = 0, p, L;
if (m == -1)
m = strlen(B);
P[0] = m;
while (a < m - 1 && B[a] == table[B[a + 1] - 'a'])
a++;
P[1] = a;
a = 1;
for (int k = 2; k < m; k++)
{
p = a + P[a] - 1;
L = P[k - a];
if ((k - 1) + L >= p)
{
int j = (p - k + 1) > 0 ? (p - k + 1) : 0;
while (k + j < m && table[B[k + j] - 'a'] == B[j])
j++;
P[k] = j;
a = k;
}
else
P[k] = L;
if (k > ((m - 1) >> 1) && k + P[k] == m)
return k;
}
return m;
}
void make_rev_table(char table[26], char rev_table[26])
{
for (int i = 0; i < 26; i++)
rev_table[table[i] - 'a'] = 'a' + i;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%s%s", table, B);
make_rev_table(table, rev_table);
int m = strlen(B);
int index = ex_preprocess(B, P, m);
for (int i = 0; i < index; i++)
printf("%c", B[i]);
for (int i = 0; i < index; i++)
printf("%c", rev_table[B[i] - 'a']);
printf("\n");
}
return 0;
}