URAL 1635

题目大意:给出一个长为n的仅由小写英文字母组成的字符串,求它的回文串划分的元素的最小个数,并按顺序输出此划分。所谓回文划分就是将原字符串拆分成若干个回文子字符串。

Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

数据规模:n<=4000。

理论基础:回文串定义:“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。

题目分析:用dp[i]表示前i个字符的回文串划分的元素的最小值。pos[i]表示取得最小值时最后一个回文串所在位置的前一个位置。odd[i]表示以s[i]为中心的最长奇数回文串的偏移量,even[i]表示以s[i]==s[i+1]为中心的最长偶数回文串的偏移量。

那么,dp[i]=min(dp[i-k]+1,k为所有的以i为结束点的回文串的长度)。

代码如下:

#include <iostream>
#include <cstring>
using namespace std;
#define N 4000
int n,tot,odd[N+1],even[N+1],pos[N+1],d[N+1],dp[N+1],print[N+1];
char s[N+5];
int main()
{
    cin >> &s[1];
    n=strlen(&s[1]);
    for (int i=1;i<=n;++i)
    {
        int l=i,r=i;
        while ((s[l] == s[r]) && l && (r <= n))
        {
            odd[i]++;
            --l,++r;
        }
        l=i,r=i+1;
        while (l && (r <= n) && (s[l] == s[r]))
        {
            even[i]++;
            --l,++r;
        }
    }
    for (int i=1;i<=n;++i)
    {
        dp[i]=dp[i-1]+1;
        pos[i]=i-1;
        for (int j=(i+1)>>1;j;--j)
        {
            int p=i-(j<<1)+1,q=i-(((j<<1)-1)>>1);
            if (odd[q] >= j)
            {
                if (dp[p]+1 < dp[i])
                {
                    dp[i]=dp[p]+1;
                    pos[i]=p;
                }
            }
            p=i-(j<<1),q=i-j;
            if (p < 0)  continue;
            if (even[q] >= j)
            {
                if (dp[p]+1 < dp[i])
                {
                    dp[i]=dp[p]+1;
                    pos[i]=p;
                }
            }
        }
    }
    cout << dp[n] << endl;
    while (n)
    {
        print[++tot]=n;
        n=pos[n];
    }
    while (tot)
    {
        for (int i=print[tot+1]+1;i<=print[tot];++i)cout<<s[i];
        --tot;
        tot?cout << ' ':cout<<'\n';
    }
    return 0;
}
其中dp方法有些变化,不过大体算法不变,参照题目分析与代码即可理解。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值