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

 

posted @ 2019-07-26 11:00  Tianwell  阅读(145)  评论(0编辑  收藏  举报