HDU-4300 Clairewd’s message (kmp或扩展kmp)
题意:给出26个字母对应的映射关系,然后给出一行string
其中有密文也有明文,明文部分是不完整的 ,输出可能组成的最短明文
思路:先将(假设整个子串为)暗文全部翻译成明文,然后将翻译后的子串与原串相连接,去匹配最长公共前后缀,这是一种思路(但是会超时)
例如: qwertyuiopasdfghjklzxcvbnm qwertabcde -> jvrkzqwert (转译后) qwertabcdejvrkzqwert 找公共前后缀长度即可 (但是注意其长度不能超过n/2最长明文长度)
所以换一种思路:我们知道密文的长度至少为n/2,先将其转为明文然后再求最长公共前缀位置kmp然后用len减去(也就是直接使用kmp来求解)
或者我们再用扩展kmp的算法,来求得模式串(转译后)与原串(转译前)的最长前缀,从而得到所能匹配的明文部分
kmp代码:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 100005 int nex[maxn]; void getnext(char t[]){//求next数组 int j,k,len; j=0; k=-1; nex[j]= k; len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){ nex[++j] =++k; } else k=nex[k]; } } int kmp(char s[],char t[]){//求子串首次出现在主串中的位置 int i,j,lens,lent; i=j=0; lens=strlen(s); lent=strlen(t); while(i<lens&&j<lent){ if(j==-1||s[i]==t[j]){ ++i; ++j; } else j=nex[j]; } return j; } int main(){ char str[27],str1[maxn],str2[maxn]; char cstr[27];//密文->明文 int t,i,len1,len2,num; scanf("%d",&t); while(t--){ scanf("%s%s",str,str1); for(i=0;i<26;++i) cstr[str[i]-'a']= 'a'+i;//转换 len1=strlen(str1); for(i=0;i<len1;++i) str2[i]=cstr[str1[i]-'a'];//全部转换为明文(假设全部为暗文) str2[i]='\0'; getnext(str2);//求子串的next数组 len2 = len1/2;//假设串中明文长度 num = kmp(str1+len1-len2,str2);//从一半后的位置开始匹配(因为可以假设暗文最短为n/2,即明文最长为n/2); printf("%s",str1); len2=len1-num;//明文补全长度(从匹配位开始到结束) for(i=num;i<len2;++i){ printf("%c",str2[i]); } printf("\n"); } return 0; }
扩展kmp
#include<iostream> #include<cstdio> #include<cstring> #define maxn 100005 using namespace std; char str1[maxn],str2[maxn]; int nex[maxn],extend[maxn]; int len1,len2; //扩展kmp void getnext(){ nex[0]=len2; int j=0,p; while(j+1<len1&&str2[j]==str2[j+1])j++; nex[1]=j; int k=1; for(int i=2;i<len2;i++){ p=nex[k]+k-1; if(i+nex[i-k]-1<p) nex[i]=nex[i-k]; else{ j = max(0,p-i+1); while(i+j<len2&&str2[i+j]==str2[j])j++; nex[i]=j; k=i; } } } void exkmp(){ getnext(); int j=0,p; int len = min(len1,len2); while(j<len&&str2[j]==str1[j])j++; extend[0]=j; int k=0; for(int i=1;i<len1;i++){ p = extend[k]+k-1; if(i+nex[i-k]-1<p) extend[i]=nex[i-k]; else{ j=max(0,p-i+1); while(i+j<len1&&j<len1&&str1[i+j]==str2[j])j++; extend[i]=j; k=i; } } } int main(){ char str[27]; char cstr[27];//密文->明文 int t,i,j,len,num; scanf("%d",&t); while(t--){ scanf("%s%s",str,str1); for(i=0;i<26;++i) cstr[str[i]-'a']='a'+i; len1=strlen(str1); for(i=0;i<len1;++i) str2[i]=cstr[str1[i]-'a'];//转译 str2[i]='\0'; len2 = strlen(str2); exkmp(); len =(len1+1)/2;//假设串中密文长度 for(i=len;i<len1;++i)//从一半+1开始看,因为密文长度大于等于一半 if(i+extend[i]==len1)break;//此时i为实际密文(明文)长度 printf("%s",str1); for(j=len1-i;j<i;++j){ printf("%c",str2[j]); } printf("\n"); } return 0; }