【扩展KMP】Clairewd’s message

Clairewd is a member of FBI. After several years concealing in BUPT, she intercepted some important messages and she was preparing for sending it to ykwd. They had agreed that each letter of these messages would be transfered to another one according to a conversion table. 
Unfortunately, GFW(someone's name, not what you just think about) has detected their action. He also got their conversion table by some unknown methods before. Clairewd was so clever and vigilant that when she realized that somebody was monitoring their action, she just stopped transmitting messages. 
But GFW knows that Clairewd would always firstly send the ciphertext and then plaintext(Note that they won't overlap each other). But he doesn't know how to separate the text because he has no idea about the whole message. However, he thinks that recovering the shortest possible text is not a hard task for you. 
Now GFW will give you the intercepted text and the conversion table. You should help him work out this problem. 

Input

The first line contains only one integer T, which is the number of test cases. 
Each test case contains two lines. The first line of each test case is the conversion table S. S[i] is the ith latin letter's cryptographic letter. The second line is the intercepted text which has n letters that you should recover. It is possible that the text is complete. 

Hint

Range of test data: 
T<= 100 ; 
n<= 100000; 

Output

For each test case, output one line contains the shorest possible complete text.

Sample Input

2
abcdefghijklmnopqrstuvwxyz
abcdab
qwertyuiopasdfghjklzxcvbnm
qwertabcde

Sample Output

abcdabcd
qwertabcde

扩展KMP中的extend[i]就是母串S【i】~S【len-1】和子串T【0】~T【len-1】的最长前缀匹配

扩展KMP资料:https://wenku.baidu.com/view/8e9ebefb0242a8956bece4b3.html

大佬博客:http://www.luyixian.cn/news_show_11906.aspx

以下题解来自大佬博客,代码来自我

题意:

给你一个对应的转换表,这个转化表是明文对应的暗文表,也就是第一字母对应‘a’,第二个字母对应‘b’,然后给一个字符串,这个字符串的前一部分是暗文后一部分是明文(将暗文翻译成明文后这两部分字符串其实是一样的),但是后面一部分的明文可能不完整了,但不知道分界线从哪里,要我们打印出完整的暗文+明文

思路:

  • 先把S全部都当作是密文的,然后把S全转换成明文,保存为 a
  • 这时,S中的密文部分就全部都变成了明文,而明文部分(不用管是什么)。
  • 然后可以发现,原来的S中的明文部分是S的后缀,而a中的明文部分是T的前缀。
  • 所以,演变成了求S[i....n]与a的最长公共前缀,就是赤裸的拓展KMP问题了。

i+extend[i] 为重复子串的长度
假设暗文和明文的分界点位置为k,发现其k+extend[k]恰好就到达了串的长度len,即k+entend[k]>=len,同时说明k前面都是暗文,那么k>=extend[k],因为extend[k](extend[k]为字符串转换前的明文后缀 与 字符串转换后的完整的明文前缀的匹配长度)最长也就是等于前面暗文的长度(明文最多跟暗文一样长),所以k一定在这当中

以下是代码,代码为自己的

首先在我WA了将近50发后才AC的情况下,我要吐槽一下HDU的题面都是一些什么鬼

#include<iostream>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
#define MAXN 1000000
char a[MAXN],b[MAXN],c[MAXN],ans[MAXN];
int nextt[MAXN],extend[MAXN];
void pre_EKMP(char x[],int m,int next[])
{
    next[0]=m;
    int j=0;
    while(j+1<m && x[j]==x[j+1])j++;
    next[1]=j;
    int k=1;
    for(int i=2; i<m; i++)
    {
        int p=next[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)next[i]=L;
        else
        {
            j=max(0,p-i+1);
            while(i+j<m && x[i+j]==x[j])j++;
            next[i]=j;
            k=i;
        }
    }
}
void EKMP(char x[],int m,char y[],int n,int next[],int extend[]) //x子串 y母串
{
    pre_EKMP(x,m,next);
    int j=0;
    while(j<n && j<m && x[j]==y[j])j++;
    extend[0]=j;
    int k=0;
    for(int i=1; i<n; i++)
    {
        int p=extend[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)extend[i]=L;
        else
        {
            j=max(0,p-i+1);
            while(i+j<n && j<m && y[i+j]==x[j])j++;
            extend[i]=j;
            k=i;
        }
    }
}
void init(){
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    memset(ans,0,sizeof(ans));
}
int main(){
    int num;
    scanf("%d",&num);
    while(num--){
        init();
        scanf("%s%s",a,b);
        map<char,char>mp;
        mp.clear();
        int len=strlen(b);
        for(int i=0;i<strlen(a);i++){
            mp[a[i]]='a'+i;
        }
        for(int i=0;i<len;i++){
            c[i]=mp[b[i]];
        }
        EKMP(c,len,b,len,nextt,extend);
        int pos=len; //特别是这里!!!我WA了20发才发现,最坏情况下,并没有发送明文
        for(int i=0;i<len;i++){
            if(i>=extend[i]&&i+extend[i]>=len){
                pos=i; //1~k-1 is secret k~len of string is message 
                break;
            }
        }
        for(int i=0;i<pos;i++){
            ans[i]=b[i];
            ans[i+pos]=mp[b[i]];
        }
        printf("%s\n",ans);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值