HDU - 4300 Clairewd’s message 拓展KMP 补齐明文,使明暗文相同

HDU - 4300 Clairewd’s message

题意:

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

思路:

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

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

数组要开大点,100010会wr

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define mod 10007
using namespace std;
typedef long long ll;
const int maxn=1000010;
int ex[maxn],nxt[maxn];
char s[maxn],a[maxn],b[maxn];
int h[maxn];  //存放明文对应的暗文的ASCII值
void getnxt(char *str)
{
    int i=0,j,po,len=strlen(str);
    nxt[0]=len;//初始化next[0]
    while(str[i]==str[i+1]&&i+1<len)//计算next[1]
    i++;
    nxt[1]=i;
    po=1;//初始化po的位置
    for(i=2;i<len;i++)
    {
        if(nxt[i-po]+i<nxt[po]+po)//第一种情况,可以直接得到next[i]的值
        nxt[i]=nxt[i-po];
        else//第二种情况,要继续匹配才能得到next[i]的值
        {
            j=nxt[po]+po-i;
            if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配
            while(i+j<len&&str[j]==str[j+i])//计算next[i]
            j++;
            nxt[i]=j;
            po=i;//更新po的位置
        }
    }
}
//计算extend数组
void EXKMP(char *s1,char *s2)
{
    int i=0,j,po,len=strlen(s1),l2=strlen(s2);
    getnxt(s2);//计算子串的next数组
    while(s1[i]==s2[i]&&i<l2&&i<len)//计算ex[0]
    i++;
    ex[0]=i;
    po=0;//初始化po的位置
    for(i=1;i<len;i++)
    {
        if(nxt[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值
        ex[i]=nxt[i-po];
        else//第二种情况,要继续匹配才能得到ex[i]的值
        {
            j=ex[po]+po-i;
            if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配
            while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]
            j++;
            ex[i]=j;
            po=i;//更新po的位置
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s %s",s,a);
        int len = strlen(a);
        for(int i=0;s[i];i++)
            h[s[i]] = i+'a';
        memset(b,0,sizeof(b));
        for(int i=0;a[i];i++)
            b[i] = h[a[i]];  //转换后的字符串
        EXKMP(a,b);
        int ma = len;   //防止明文长度为 0 的情况
        for(int i=0;i<len;i++)
        {
            if(i+ex[i]>=len && i>=ex[i])
            {
                ma = i;
                break;
            }
        }
        memset(b,0,sizeof(b));
        for(int i=0;i<ma;i++)
        {
            b[i] = a[i];  //暗文
            b[ma+i] = h[a[i]];  //明文
        }
        puts(b);
    }
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值