OPENJUDGE NOI 8471 切割回文

来源:互联网 发布:java 开源打码插件 编辑:程序博客网 时间:2024/06/10 06:31

OPENJUDGE NOI 8471 切割回文

总时间限制: 1000ms 内存限制: 65536kB
描述
阿福最近对回文串产生了非常浓厚的兴趣。
如果一个字符串从左往右看和从右往左看完全相同的话,那么就认为这个串是一个回文串。例如,“abcaacba”是一个回文串,“abcaaba”则不是一个回文串。
阿福现在强迫症发作,看到什么字符串都想要把它变成回文的。阿福可以通过切割字符串,使得切割完之后得到的子串都是回文的。
现在阿福想知道他最少切割多少次就可以达到目的。例如,对于字符串“abaacca”,最少切割一次,就可以得到“aba”和“acca”这两个回文子串。
输入
输入的第一行是一个整数 T (T <= 20) ,表示一共有 T 组数据。
接下来的 T 行,每一行都包含了一个长度不超过的 1000 的字符串,且字符串只包含了小写字母。
输出
对于每组数据,输出一行。该行包含一个整数,表示阿福最少切割的次数,使得切割完得到的子串都是回文的。


【题目分析】
第一次用Markdown写博客,感觉好赞。
收一收激动的心情,我来写题解,首先看到这道题就是很简单的背包类型的动态规划,显然是要不留缝隙地填满的。很容易想到动态规划,如果不预处理,是N^3的,然后加一个预处理复杂度就是N^2了,详见代码1.(其实这个代码是从隔壁抄来的@yhx相信他不会介意的)30ms

#include<cstdio>#include<cstring>bool b[1010][1010];int dp[1010];char s[1010];int min(int x,int y){    if (x<y) return x;    return y; } int main(){    int i,j,k,l,m,n,p,q,x,y,z,T,mid;    scanf("%d",&T);    while (T--)    {        memset(b,0,sizeof(b));        memset(dp,0x42,sizeof(dp));        scanf("%s",s+1);        l=strlen(s+1);        for (k=1;k<=l;k++)          for (i=1;i+k-1<=l;i++)          {            j=i+k-1;            if (k<=2)            {                if (s[i]==s[j]) b[i][j]=1;            }            else            {                if (b[i+1][j-1]&&s[i]==s[j]) b[i][j]=1;            }          }        dp[0]=0;        for (i=1;i<=l;i++)          for (j=0;j<=i;j++)            if (b[j][i]) dp[i]=min(dp[i],dp[j-1]+1); printf("%d\n",dp[l]-1);    }}
然后我脑洞大开,可以把预处理降到N的级别,显然对总体的时间复杂度并没有什么卵用。以下是优化过的代码二。然而事实证明,比上一种方法快了两倍,#(滑稽)只有10ms。
#include <cstdio>#include <string>#include <cstring>#include <iostream>#include <algorithm>using namespace std;int r[2010],dp[2010];char s[2010],ch[2010];inline int min(int a,int b){return a>b?b:a;}inline bool pd(int l,int e){    if ((r[l+e]*2-1)/2>=e-l+1) return true;    else return false;}int main(){    int tt;    scanf("%d",&tt);    while (tt--)    {        memset(r,0,sizeof r);        memset(dp,0x3f,sizeof dp);        memset(r,0,sizeof r);        dp[0]=0;        scanf("%s",ch+1);        int l=strlen(ch+1);        s[1]='#';s[2*l+2]='#';//处理一下回文串         for (int i=1;i<=l;++i)        {            s[i*2]=ch[i];            s[i*2|1]='#';        }        int id=0,mx=0;//高逼格manacher         r[0]=1;        for (int i=1;i<=2*l+1;++i){            if (mx>i) r[i]=min(r[2*id-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],id=i;        }        for (int i=1;i<=l;++i)//动态规划             for (int j=1;j<=i;++j)                if (pd(j,i)) dp[i]=min(dp[i],dp[j-1]+1);        printf("%d\n",dp[l]-1);    }}
0 2