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; }