题意
给定一个密文的转换表,即 26 个字母原文与密文的映射。然后,给定一个字符串,由密文与明文混合而成,其中前半部分为密文,后半部分为明文,明文可能有缺失(可能缺失全部),但密文一定为完整的。现在,求出给定的字符串(密文+明文)对应的完整的字符串中最短的那一个。
思路
先将给定的字符串按照转换表,翻译成新的字符串,其中前半部分密文被翻译为明文,后半部分明文被翻译成密文。现在,我们得到了一个明文完整而密文缺失的字符串。接下来,就只需要比对原文的后缀(明文)与翻译后的字符串的前缀(明文)的最长相等子串,这就能够使得完整的字符串最短。
要求出最长相等字串,有两种方法,一种是字符串哈希,使得两个子串能够在 O(1) 的时间内进行比较;一种是扩展 KMP。扩展 KMP 还没补,就先放字符串哈希的版本。
代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5+5;
int T;
string s, t;
int c[30];
ull h1[maxn], p1[maxn];
ull h2[maxn], p2[maxn];
void initHash(string ss, ull h[], ull p[])
{
int len = ss.length();
h[0] = 0;
for(int i = 1; i < len; ++i)
h[i] = h[i-1]*131 + ss[i]-'a'+1;
p[0] = 1;
for(int i = 1; i < len; ++i)
p[i] = p[i-1] * 131;
}
ull getHash(int l, int r, ull h[], ull p[])
{
ull ret;
ret = h[r] - h[l-1]*p[r-l+1];
return ret;
}
void read()
{
cin >> s >> t;
t = " " + t;
}
void init()
{
for(int i = 0; i < s.length(); ++i)
c[s[i]-'a'] = i;
string tt = " ";
for(int i = 1; i < t.length(); ++i)
tt += c[t[i]-'a'] + 'a';
initHash(t, h1, p1);
initHash(tt, h2, p2);
}
void solve()
{
init();
int tLen = t.length()-1;
int ans = tLen;
for(int i = tLen; i < tLen*2; ++i)
{
if(i & 1)
continue;
ull hashVal1 = getHash(1, tLen-i/2, h2, p2);
ull hashVal2 = getHash(i/2+1, tLen, h1, p1);
if(hashVal1 == hashVal2)
{
ans = i / 2;
break;
}
}
for(int i = 1; i <= ans; ++i)
cout << t[i];
for(int i = 1; i <= ans; ++i)
cout << char(c[t[i]-'a'] + 'a');
cout << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> T;
while(T--)
{
read();
solve();
}
return 0;
}