HDU 4300 Clairewd’s message(扩展KMP)
http://acm.hdu.edu.cn/showproblem.php?pid=4300
题意:首先有一个字母的转换表,就是输入的第一行的字符串,就是'a'转成第一个字母,'b'转成转换表的第二个字母·······
然后下面一个字符串是密文+明文的形式的字符串。
就是说前后两段是重复的,只不过通过转换表转换了下。
而且后面一段可能不完整。我们现在要找到这个串可能的最短密文+明文.
分析:
首先串S的最短密文+明文可能就是本身,也就是说S正好可以分成两段,前一段是密文可以通过转换表转成与后半段正好相同的明文.
且密文+明文的长度最长不会超过2*T.(即串S完全是密文)
由于前半段是密文,后半段是明文.假设输入串为S,我们将S完全映射之后得到串T,然我们用串T作为模式串去匹配串S,调用扩展KMP算法,求出以S[i]为开头的前缀与串T的前缀最多匹配长度.那么只要有一个超过串S一半以上的位置的i,以s[i]为头的前缀与T前缀匹配到底,那么这个i就是S中明文的开始.(仔细想想是不是)
AC代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
const int MAXN=100000+1000;
char S[MAXN],T[MAXN];
int B[MAXN],A[MAXN];
int n,m;
void EKMP()
{
int j=0;
while(j+1<m && T[0+j]==T[1+j])
j++;
A[1]=j;
int k=1;
for(int i=2;i<m;i++)
{
int len=k+A[k]-1,L=A[i-k];
if(L<len-i+1)
A[i]=L;
else
{
j=max(0,len-i+1);
while(i+j<m && T[0+j] ==T[i+j])
j++;
A[i]=j;
k=i;
}
}
j=0;
while(j<n && j<m && S[0+j]==T[0+j])
j++;
B[0]=j;
k=0;
for(int i=1;i<n;i++)
{
int len=k+B[k]-1,L=A[i-k];
if(L<len-i+1)
B[i]=L;
else
{
j=max(0,len-i+1);
while(i+j<n && j<m && S[i+j]==T[j])
j++;
B[i]=j;
k=i;
}
}
}
int main()
{
int kase;
scanf("%d",&kase);
while(kase--)
{
map<char ,char> mp;
char str[30];
scanf("%s%s",str,S);
int len1=strlen(str);
m=n=strlen(S);
for(int i=0;i<len1;i++)
mp[str[i]]=i+'a';
for(int i=0;i<n;i++)
T[i]=mp[S[i]];
T[n]=0;
EKMP();
int i;
for(i=(n+1)/2;i<n;i++)
if(i+B[i]==n)
break;
for(int j=0;j<i;j++)
printf("%c",S[j]);
for(int j=0;j<i;j++)
printf("%c",mp[S[j]]);
printf("\n");
}
return 0;
}