题目大意:题目给你两个串,第一个串是翻译串。第二个串是一个由密文和明文组成的串,密文一定是完整的而明文不一定甚至可能没有。最后让你输出完整的密文+明文串。
解题思路:
我们首先把给出的字符串str全部翻译成明文,保存在字符串pattern中(为什么取这个名字等下有说明,先记一下)。(str仍保留初始字符)
再由题意密文全部给出可知,str中至少前面一半的字符是密文,后面一半存在一个最长后缀是明文(这个后缀也可以为空串),我们把后半段str全部存在字符串s中。
此时我们有3个字符串: 1.str 保存题目给的原始密文
2.pattern保留str翻译一遍的字符(密文变成了明文,明文变成了其他不用管的字符)
3.s 保存str中后半段字符
接下来只需要找出s满足与str的前缀匹配的最长后缀,这个长度length就是str后半段包含的明文字符数目,比如 str=abcdeab ,pattern=abcd×××(后三个不用管),s=eab ,则s满足要求的后缀就是ab,length=2,就可知还剩4-2个(4是str的一半)明文没有输出。
而要找到s满足与str的前缀匹配的最长后缀是经典的exkmp算法问题,但是这里我们也可以用kmp算法求解 。
方法一、 在s中找pattern的匹配串(它们的名字就是这样取的qaq),经典kmp算法,但是这里s长度是小于pattern的,所以最终s的指针走完了,pattern的指针走过的长度就是最长匹配后缀length
方法二、把pattern,s拼接起来(new_str=pattern+s,len=new_str.size() ),求出new_str的next数组,next[len-1]就是length,但是如果next[len-1]大于s或的pattern的长度,需要回溯使之不大于,具体见代码
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
#define MAX 200500
using namespace std;
//把初始字符串str翻译一遍成字符串pattern,同时把初始str后一半拿出来给s,用pattern去匹配s,s为主串,返回能匹配到的最长长度
int f1(string s,string pattern)//s后缀 pattern前缀,拼起来利用kmp的match数组
{
string str=pattern+s;//拼接起来求match数组
int match[MAX];
match[0]=-1;
int len=(int)str.size();
for(int i=1;i<len;i++)
{
int j=match[i-1];
while(j>=0 && str[j+1]!=str[i]) j=match[j];
if(str[j+1]==str[i]) match[i]=j+1;
else match[i]=-1;
}
//可能有重合,要回溯到小于拼接前
while(match[len-1]>= (int)s.size() || match[len-1]>= (int)pattern.size()) match[len-1]=match[match[len-1]];
return match[len-1]+1;
}
int f2(string s,string pattern)//在s中用kmp匹配pattern的最长前缀
{
int match[MAX];
match[0]=-1;
int len1=s.size(),len2=pattern.size();
for(int i=1;i<len2;i++)
{
int j=match[i-1];
while(j>=0 && pattern[j+1]!=pattern[i]) j=match[j];
if(pattern[j+1]==pattern[i]) match[i]=j+1;
else match[i]=-1;
}
int p1=0,p2=0;
while(p1<len1 && p2<len2)
{
if(pattern[p2]==s[p1]) p1++,p2++;
else if(p2>0) p2=match[p2-1]+1;
else p1++;
}
return p2;//此时由于s的后缀是pattern的一部分,pattern肯定还没有走完,而s已经找完了,p2就是s匹配pattern前缀的最大后缀
}
int main()
{
int t;
cin>>t;
while(t--)
{
string str,pattern,s;
string sm;
cin>>sm>>str;
int len=(int)str.size();
int alpha[129]={0};
for(int i=0;i<26;i++) alpha[sm[i]]=i;
for(int i=0;i<len;i++) pattern+='a'+alpha[str[i]];
int half=len/2;
if(len&1) half++;//前半段暗文长度
s=str.substr(half);
//接下来求出s的后缀与pattern的前缀最长公共长度就ok
//可以用exkmp ,也可拼接起来 ,也可直接上kmp
int length=f1(s,pattern);
if(length*2==len) cout<<str<<'\n';
else
{
cout<<str<<pattern.substr(length,len-length*2)<<'\n';
}
}
system("pasue");
return 0;
}