HDU-4300:Clairewd’s message(最长前后缀匹配问题kmp做法)

题目链接

题目大意:题目给你两个串,第一个串是翻译串。第二个串是一个由密文和明文组成的串,密文一定是完整的而明文不一定甚至可能没有。最后让你输出完整的密文+明文串。

解题思路:
我们首先把给出的字符串str全部翻译成明文,保存在字符串pattern中(为什么取这个名字等下有说明,先记一下)。(str仍保留初始字符)
再由题意密文全部给出可知,str中至少前面一半的字符是密文,后面一半存在一个最长后缀是明文(这个后缀也可以为空串),我们把后半段str全部存在字符串s中。
此时我们有3个字符串: 1.str 保存题目给的原始密文
2.pattern保留str翻译一遍的字符(密文变成了明文,明文变成了其他不用管的字符)
3.s 保存str中后半段字符
接下来只需要找出s满足与str的前缀匹配的最长后缀,这个长度length就是str后半段包含的明文字符数目,比如 str=abcdeab ,pattern=abcd×××(后三个不用管),s=eab ,则s满足要求的后缀就是ab,length=2,就可知还剩4-2个(4是str的一半)明文没有输出。
而要找到s满足与str的前缀匹配的最长后缀是经典的exkmp算法问题,但是这里我们也可以用kmp算法求解 。
方法一、 在s中找pattern的匹配串(它们的名字就是这样取的qaq),经典kmp算法,但是这里s长度是小于pattern的,所以最终s的指针走完了,pattern的指针走过的长度就是最长匹配后缀length
方法二、把pattern,s拼接起来(new_str=pattern+s,len=new_str.size() ),求出new_str的next数组,next[len-1]就是length,但是如果next[len-1]大于s或的pattern的长度,需要回溯使之不大于,具体见代码

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
#define MAX 200500
using namespace std;
//把初始字符串str翻译一遍成字符串pattern,同时把初始str后一半拿出来给s,用pattern去匹配s,s为主串,返回能匹配到的最长长度

int f1(string s,string pattern)//s后缀  pattern前缀,拼起来利用kmp的match数组
{
    string str=pattern+s;//拼接起来求match数组
    int match[MAX];
    match[0]=-1;
    int len=(int)str.size();
    for(int i=1;i<len;i++)
    {
        int j=match[i-1];
        while(j>=0 && str[j+1]!=str[i]) j=match[j];
        if(str[j+1]==str[i]) match[i]=j+1;
        else match[i]=-1;
    }
    //可能有重合,要回溯到小于拼接前
    while(match[len-1]>= (int)s.size() || match[len-1]>= (int)pattern.size()) match[len-1]=match[match[len-1]];
    return match[len-1]+1;
}
int f2(string s,string pattern)//在s中用kmp匹配pattern的最长前缀
{
    int match[MAX];
    match[0]=-1;
    int len1=s.size(),len2=pattern.size();
    for(int i=1;i<len2;i++)
    {
        int j=match[i-1];
        while(j>=0 && pattern[j+1]!=pattern[i]) j=match[j];
        if(pattern[j+1]==pattern[i]) match[i]=j+1;
        else match[i]=-1;
    }
    int p1=0,p2=0;
    while(p1<len1 && p2<len2)
    {
        if(pattern[p2]==s[p1]) p1++,p2++;
        else if(p2>0) p2=match[p2-1]+1;
        else p1++;
    }
    return p2;//此时由于s的后缀是pattern的一部分,pattern肯定还没有走完,而s已经找完了,p2就是s匹配pattern前缀的最大后缀
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        string str,pattern,s;
        string sm;
        cin>>sm>>str;
        int len=(int)str.size();
        int alpha[129]={0};
        for(int i=0;i<26;i++) alpha[sm[i]]=i;
        for(int i=0;i<len;i++) pattern+='a'+alpha[str[i]];
        int half=len/2;
        if(len&1) half++;//前半段暗文长度
        s=str.substr(half);
        //接下来求出s的后缀与pattern的前缀最长公共长度就ok
        //可以用exkmp ,也可拼接起来 ,也可直接上kmp
        int length=f1(s,pattern);
        if(length*2==len) cout<<str<<'\n';
        else
        {
            cout<<str<<pattern.substr(length,len-length*2)<<'\n';
        }        
    }
    system("pasue");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值