Codeforces 30E Tricky and Clever Password Manacher + KMP + 前缀和

12 篇文章 0 订阅
8 篇文章 0 订阅

题目大意:

就是现在有一个长度为奇数的个回文串串T被加密成了 A + prefix + B + middle + C + suffix的形式, 其中 T = prefix + middle + suffix , 这六个部分可以是空的, 现在对于给出的加密后的串求原串T的最大可能长度, 其中middle长度也是奇数, preifx长度和suffix长度相等, 输入的字符串长度<= 10^5


大致思路:

首先很容易观察到middle的中心就是T的中心, middle也是回文串, 先Manacher处理出每个字符为中心的回文半径, 那么枚举T的正中心将[i - R[i] + 1, i + R[i] - 1]作为middle一定是以i为中心下的最优解, 然后考虑最大化suffix和prefix, 用KMP预处理出以 j 位置字符为结尾的能和suffix匹配最大长度的字符串的长度, 即 match[j] 表示 T[j - k + 1, ... j - 1, j] == T'[0...k - 1] 的最大的k, T‘表示T的反向字符串, 那么预处理前缀和dp[i] = max(dp[i - 1], match[i])来表示区间[1, i]结尾的字符串中能匹配的最大长度, 于是剩下的就很明了了= =

这个思想是参见2014年国家集训队徐毅的论文上的, 可以去参考一下那篇论文


代码如下:

Result  :  Accepted     Memory  :  2164 KB     Time  :  62 ms

/*
 * Author: Gatevin
 * Created Time:  2015/3/29 9:26:17
 * File Name: Chitoge_Kirisaki.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

#define maxn 100010

char s[maxn];
int R[maxn];
char rev[maxn];
int next[maxn];
int match[maxn];
int dp[maxn];
int tail[maxn];
void Manacher(char *s, int *R, int n)
{
    int mx = 0, p = 0;
    R[0] = 1;
    for(int i = 1; i <= n; i++)
    {
        if(mx > i) R[i] = min(R[2*p - i], mx - i);
        else R[i] = 1;
        while(s[i - R[i]] == s[i + R[i]])
            R[i]++;
        if(i + R[i] > mx)
            mx = i + R[i], p = i;
    }
    return;
}

void KMP(char *s, char *rev, int n)
{
    memset(next, 0, sizeof(next));
    for(int i = 1; i < n; i++)
    {
        int j = i;
        while(j > 0)
        {
            j = next[j];
            if(rev[i] == rev[j])
            {
                next[i + 1] = j + 1;
                break;
            }
        }
    }
    memset(match, 0, sizeof(match));
    for(int i = 1, j = 0; i < n + 1; i++)
        if(s[i] == rev[j])
            j++, match[i] = j;
        else
            while(j > 0)
            {
                j = next[j];
                if(s[i] == rev[j])
                {
                    j++;
                    match[i] = j;
                    break;
                }
            }
    return;
    
}

vector <pair<int, int> > pos;

int main()
{
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    s[0] = '@', s[n + 1] = '$';
    Manacher(s, R, n + 1);
    for(int i = 0; i < n; i++)
        rev[i] = s[n - i];
    rev[n] = '\0';
    KMP(s, rev, n);
    int maxl = 0;
    dp[1] = match[1];
    tail[1] = 1;
    for(int i = 1; i <= n; i++)
    {
        if(dp[i - 1] < match[i])
            dp[i] = match[i], tail[i] = i;
        else dp[i] = dp[i - 1], tail[i] = tail[i - 1];//tail[i]记录dp[i]对应最优解的串的末尾
    }
    for(int i = 1; i <= n; i++)//枚举middle中心
    {
        int middle = 2*R[i] - 1;
        int pre_suf = min(dp[i - R[i]], n - (i + R[i] - 1));//prefix或suffix长度
        if(middle + 2*pre_suf > maxl)
        {
            pos.clear();
            maxl = middle + 2*pre_suf;
            if(pre_suf > 0)
               pos.push_back(make_pair(tail[i - R[i]] - pre_suf + 1, pre_suf));
            pos.push_back(make_pair(i - R[i] + 1, 2*R[i] - 1));
            if(pre_suf > 0)
                pos.push_back(make_pair(n - pre_suf + 1, pre_suf));
        }
    }
    printf("%d\n", (int)pos.size());
    for(unsigned int i = 0; i < pos.size(); i++)
        printf("%d %d\n", pos[i].first, pos[i].second);
    return 0;
}


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值