哈理工OJ 1847 擦除字符串(状压DP)

来源:互联网 发布:英魂之刃刷点卷软件 编辑:程序博客网 时间:2024/06/05 08:50

原题地址

擦除字符串Time Limit: 5000 MSMemory Limit: 32768 KTotal Submit: 32(17 users)Total Accepted: 16(13 users)Rating: Special Judge: NoDescription给出一个字符串s,我们每一步可以擦除当前字符串中的一个回文子串。现在问最少需要多少步才能把整个字符串擦除。
例如我们能在一步里面擦除abcba从axbyczbea并且得到xyze。
Input第一行是一个整数T,代表T组测试数据,对于每组测试数据输入一个长度<=16的字符串。Output对于每组测试数据输出最少的步数。Sample Input2
aa
abbSample Output1
2

状压DP。

我们可以反过来看,将题目看成是从空串添加回文串,直至成为原串。


我们先枚举出原串中所有的回文串,虽然时间复杂度较高,但是字符串不长,所以无碍。

用一个dug数组,将回文串状态压缩成一个数,i 状态是回文串,则dug[i] = 1。比如:


原串:f g h j i u h g f

那么f g h i h g f  是一个回文串

二进制数 1 1 1 0 1 0 1 1 1 = 471

记dug[ 471 ] = 1


记增添回文串到状态i的最小次数为dp[ i ]

然后我们将每个状态进行枚举,如果这个状态 q 与前一个状态 i 比较下,是回文串的话,那么取最小值,即dp[q]=min(dp[q],dp[i]+1)。


第一次用的是记忆化搜索,严重超时!但是代码中记忆化搜索的部分没删,注释掉了。


AC代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;char s[17];int dp[1<<17];bool dug[1<<17];void check(int len){    for(int i=1;i<(1<<len);i++)    {        int flag=0;        int k=len;        for(int j=0;j<k;)        {            while(((1<<j)&i)==0)            {                j++;                if(j>len)                {                    break;                }            }            while(((1<<k)&i)==0)            {                k--;                if(k<0)                {                    break;                }            }            if(j<=k)            {                if(s[j]!=s[k])                {                    flag=1;                    break;                }                j++;                k--;            }        }        if(!flag)        {            dug[i]=1;        }    }    return;}void dfs(int z,int ans,int len,int maxlen){    if(z==0)    {        if(dp[z]>ans)            dp[z]=ans;        return;    }    else    {        for(int i=len;i>=0;i--)        {            if((z&(1<<i))>0)            {                len=i+1;                break;            }        }        for(int i=1;i<(1<<len);i++)        {            if(dug[i]&&(((z^i)&(~z))==0))            {                //printf("$%d\n",i);                if(dp[z^i]>ans+1)                {                    dp[z^i]=ans+1;                }                else                {                    ans=dp[z^i]-1;                }                dfs(z^i,ans+1,len,maxlen);            }        }    }}/*101101   z101000   i000101 z^i*//*011010  ~z100101   z000111   i100010 z^i*/int main(){    int t;    scanf("%d",&t);    getchar();    while(t--)    {        scanf("%s",s);        int len =strlen(s);        memset(dug,0,sizeof(dug));        memset(dp,0x3f3f3f3f,sizeof(dp));        check(len);        //dfs((1<<len)-1,0,len,len);//记忆化搜索会超超超超时……        int end=1<<len;        dp[0]=0;        for(int i=0;i<end;i++)        {            int x=i^(end-1);            for(int j=x;j!=0;j=(j-1)&x)            {                if(dug[j]==0)continue;                int q=i|j;                dp[q]=min(dp[q],dp[i]+1);            }        }        printf("%d\n",dp[end-1]);    }    return 0;}


0 0