USACO 1.3.4 Calf Flac

Description
据说如果你给无限只母牛和无限台巨型便携式电脑(有非常大的键盘),那么母牛们会制造出世上最棒的回文。你的工作就是去寻找这些牛制造的奇观(最棒的回文)。 在寻找回文时不用理睬那些标点符号、空格(但应该保留下来以便做为答案输出),只用考虑字母’A’-‘Z’和’a’-‘z’。要你寻找的最长的回文的文章是一个不超过20,000个字符的字符串。 我们将保证最长的回文不会超过2,000个字符(在除去标点符号、空格之前)。

Input
输入文件不会超过20,000字符。这个文件可能一行或多行,但是每行都不超过80个字符(不包括最后的换行符)。

Output
输出的第一行应该包括找到的最长的回文的长度。 下一行或几行应该包括这个回文的原文(没有除去标点符号、空格),把这个回文输出到一行或多行(如果回文中包括换行符)。 如果有多个回文长度都等于最大值,输出最前面出现的那一个。

Sample Input
Confucius say: Madam, I’m Adam.
Sample Output
11
Madam, I’m Adam


思路:
该题最核心的问题应该就是最长回文子串了吧,再在这基础上做一些变形。
最长回文子串要用Manacher算法(感谢Manacher)。可是Manacher算法要的是有效字符组成的字符串,所以,在用Manacher算法之前,要对原串进行一下处理,处理后得到处理串t,同时还要用一个p数组记录字符串t中每个字符在原串的位置,(这个有什么用了?之后会用到的)。Manacher算法会得到最长回文字子串的中心以及半径,这样我们可以求出最长回文字子串在串t中的起点和终点。此时把起点和终点当作p数组的下标就可以得到最长回文子串在原串中的起点和终点,这样得到的起点和终点构成的子字符串就是题目所要求的结果了。
这个题的原串有多行,要用getchar 搭配EOF来处理,一开始特懵逼。

#include <iostream>
#include <cstdio>
#include <vector>
#define N 20004
using namespace std;
int resLen, resCenter;
void Manacher(string s)
{
    string t;
    t = "$#";
    for(int i = 0; i < s.size(); i++)
    {
        t += s[i];
        t += '#';
    }
    vector<int>p(t.size(), 0);
    int id = 0, mx = 0;
    for(int i = 1; i < t.size(); i++)
    {
        p[i] = mx>i ? min(p[2 * id - i], mx - i) : 1;
        while(t[i + p[i]] == t[i - p[i]])
            p[i]++;
        if( mx < i + p[i] )
        {
            id = i;
            mx = i + p[i];
        }
        if(resLen < p[i])
        {
            resLen = p[i];
            resCenter = i;
        }
    }
}
int main()
{
    int cnt=0;
    char c,a[N]= {0};
    string s;
    while((c=getchar())!=EOF)
        a[cnt++]=c;
    int p[N]= {0},g=0;
    for(int i = 0; i < cnt; i++)
    {
        if( a[i] >= 'A' && a[i] <= 'Z' )
        {
            p[g++]=i;
            s.push_back(a[i]+32);
        }
        else if(a[i] >= 'a' && a[i] <= 'z')
        {
            p[g++]=i;
            s.push_back(a[i]);
        }
    }
    Manacher(s);
    int sta = (resCenter - resLen) / 2, len = resLen - 1;
    printf("%d\n",len);
    for(int i = p[sta]; i <= p[sta + len - 1]; i++)
        printf("%c",a[i]);
    printf("\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值