URAL 1635

来源:互联网 发布:传智播客php笔记 编辑:程序博客网 时间:2024/05/29 11:10

题目大意:给出一个长为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 4000int 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方法有些变化,不过大体算法不变,参照题目分析与代码即可理解。
by:Jsun_moon http://blog.csdn.net/Jsun_moon
原创粉丝点击