HDU 4300 Clairewd’s message 扩展kmp || kmp

7 篇文章 0 订阅

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4300

题意:有一段字符串由密文和明文组成,密文是完好的,明文可能是残缺的。首先给出密文转换表,然后给出密文明文字符串,然后复原出最短的密文明文字符串(补上明文)

思路:可以用扩展kmp求解。首先,明文的起始位置一定在字符串后半段(密文 >= 明文),于是把前半段转换成明文,然后用扩展kmp求每一个位置和字符串的最长公共前缀长度,后半段中满足next[i] + i = len的i,一定是明文的起始位置,因为这代表从i开始以后完全重合与字符串首部的一段字符。注意明文可能完全丢失

扩展kmp

#include <bits/stdc++.h>

using namespace std;

const int N = 100000 + 10;

char s1[30], s2[N], s3[N], table[30];
int Next[N];

void get_next(char *pat)
{
    int len = strlen(pat);
    Next[0] = len;
    int k = 0;
    while(k + 1 < len && pat[k] == pat[k+1]) ++k;
    Next[1] = k;
    k = 1;
    for(int i = 2; pat[i]; i++)
    {
        if(i + Next[i-k] - 1 < k + Next[k] - 1) Next[i] = Next[i-k];
        else
        {
            int j = k + Next[k] - i;
            if(j < 0) j = 0;
            while(i + j < len && pat[j] == pat[i+j]) ++j;
            Next[i] = j;
            k = i;
        }
    }
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%s%s", s1, s2);
        for(int i = 0; i < 26; i++) table[s1[i]-'a'] = i + 'a';//密文字符对应的明文字符
        int len = strlen(s2);
        for(int i = 0; i < (len+1)/2; i++) s3[i] = table[s2[i]-'a'];
        for(int i = (len+1)/2; i <= len; i++) s3[i] = s2[i];
        get_next(s3);
        int ans = len;//当找不到公共前缀时,意味整个串都是密文,所以预先给res赋值为len
        for(int i = (len+1)/2; i < len; i++)
            if(i + Next[i] == len)//条件成立说明从i位置开始到末尾是字符串的前缀,认为从i开始是明文
            {
                ans = i; break;
            }
        printf("%s", s2);
        for(int i = len-ans; i < ans; i++) printf("%c", table[s2[i]-'a']);
        printf("\n");
    }
    return 0;
}

kmp做法。思路也是求后半段中和字符串的最长公共前缀长度

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <map>
#include <vector>
#include <cstring>
#include <cmath>
#include <set>
using namespace std;

const int N = 100100;

char s1[100], tab[100], s2[N], s3[N];
int Next[N];

void get_next(char *pat)
{
    int i = 0, j = -1;
    Next[0] = -1;
    while(pat[i])
    {
        if(j == -1 || pat[i] == pat[j])
        {
            ++i, ++j;
            if(pat[i] != pat[j]) Next[i] = j;
            else Next[i] = Next[j];
        }
        else j = Next[j];
    }
}
int kmp(char *ori, char *pat)
{
    get_next(pat);
    int i = (strlen(ori)+1) / 2, j = 0;
    while(ori[i])
    {
        if(j == -1 || ori[i] == pat[j]) ++i, ++j;
        else j = Next[j];
    }
    return j;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%s%s", s1, s2);
        int len = strlen(s2);
        for(int i = 0; i < 26; i++)
            tab[s1[i]-'a'] = i + 'a';
        for(int i = 0; i < (len + 1) / 2; i++)
            s3[i] = tab[s2[i]-'a'];
        for(int i = (len + 1) / 2; i <= len; i++)
            s3[i] = s2[i];
        int res = kmp(s2, s3);
        printf("%s", s2);
        for(int i = res; i < len - res; i++)
            printf("%c", tab[s2[i]-'a']);
        printf("\n");
    }
    return 0;
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值