[HDU 5340] Three Palindromes 最长回文串

http://acm.hdu.edu.cn/showproblem.php?pid=5340

题意:问是否能将原串分解成三个回文的非空字符串。

思路:用Manmcher算法先求出原串中所有的回文字串,保存在pa[i] 里面,pa[i] 表示以 i 为中点的回文字符串向左右延伸的最长长度,开两个数组pre[i], suf[i] 分别表示 1- i , i - n能否形成回文串,上面都能在O(n)的时间求出,然后就是枚举中间的回文字符串的中点 i ,然后就是判断pre[i-pa[i]…i-1] 和 suf[i+1…i+pa[i]] 能否同时为真。

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int maxn = 20010;

int pa[maxn<<1]; //以 i 为中点的最长回文子串的长度
char temp[maxn], str[maxn<<1]; //输入字符串和处理后的字符串
bool pre[maxn<<1], suf[maxn<<1]; //1-i是否回文,i-n是否回文

void Manacher(int len, char *str, int *pa) //Manacher求出所有的回文子串
{
    int id, maxlen = 0;
    for(int i = 0; i < len; i++){
        pa[i] = 1;
        if(maxlen > i)
            pa[i] = maxlen - i < pa[2*id-i] ? maxlen - i : pa[2*id-i];
        while(str[i+pa[i]] == str[i-pa[i]]){
            pa[i]++;
        }
        if(i + pa[i] > maxlen){
            id = i;
            maxlen = i + pa[i];
        }
    }
}

//枚举第二个回文子串的中点
bool solve(int len, char *str, int *pa, bool *pre, bool *suf)
{
    for(int i = 4;i < len - 3;i++) //枚举第二个子串中点
    {
        //因为第一个和第三子串都是'#'结尾所以第一个子串的结尾和第三个子串的开始都要是'#'.
        for(int k = (str[i] == '#' ? 2 : 1); k <= pa[i] ; k += 2) {
            if(pre[i - k]&suf[i + k] && i - k > 1 && i + k < len-1)
                return true ;
        }
    }
    return false ;
}


int main()
{
    int Test;
    scanf("%d", &Test);
    while(Test--)
    {
        scanf("%s", temp);
        int len = strlen(temp);
        for(int i = 0; i < len; i++) //Manacher只能处理奇串,将奇串和偶串都处理成奇串
        {
            str[2*i+1] = '#';
            str[2*i+2] = temp[i];
        }
        len = 2 * len + 2;
        str[0] = '$';  //预防出界,字符串从 1 开始
        str[len] = str[len-1] = '#';
        Manacher(len, str, pa); // manacher算法
        memset(pre, false, sizeof(pre));
        memset(suf, false, sizeof(suf));
        for(int i = 1; i < len; i++)//求出pre 和 suf
        {
            if(i == pa[i])
                pre[i+pa[i]-1] = true;
            if(pa[i] == len - i)
                suf[i-pa[i]+1] = true;
        }
        if(solve(len, str, pa, pre, suf))
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

achonor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值