题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4300
题目意思:
给一个26个字母a-z对应的密码,给一个字符串,前面为密码后面为明码,让你找出最短的完整的信息,使前面一段时密码,后面一段时明码,而且不能重叠,中间不能有多的。如果不能找到,自己构造出一个。
解题思路:
next[i]表示前缀密码和后缀明码最大的匹配长度。
注意最后next[n]可能大于n/2此时中间有重叠的地方,需要不断地往前移。
处理一下,一遍就可以搞定。
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;
#define Maxn 110000
char save[Maxn],change[27];
int next[Maxn],n;
char ss[27];
void getnext()
{
int j=0;
next[1]=0;
for(int i=2;i<=n;i++)
{
while(j>0&&save[j+1]-change[save[i]-'a'])
j=next[j];
if(save[j+1]==change[save[i]-'a'])
j++;
next[i]=j;
}
//printf("%d\n",next[n]);
if(next[n]<n/2)
return ;
int temp=n;
while(next[n]>n/2) //大于一半,一直往前移
{
temp=next[temp];
next[n]=temp;
} //如果恰好有一半,就不用管了,这是最大的
if(next[n]<n/2) //看能否再凑成一个
{
if(save[next[n]+1]==change[save[n]-'a'])
next[n]++;
}
return ;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s%s",change,save+1);
for(int i=0;i<26;i++)
ss[change[i]-'a']=i+'a';
n=strlen(save+1);
getnext();
// printf("next:%d\n",next[n]);
if(n-next[n]==next[n])
printf("%s\n",save+1);
else
{
printf("%s",save+1);
for(int i=next[n]+1;i<=n-next[n];i++)
putchar(ss[save[i]-'a']);
putchar('\n');
}
//printf("%d\n",n);
}
return 0;
}