哈理工OJ 1847 擦除字符串(狀壓DP)

原題地址

擦除字符串
Time Limit: 5000 MS Memory Limit: 32768 K
Total Submit: 32(17 users) Total Accepted: 16(13 users) Rating: Special Judge: No
Description
給出一個字符串s,我們每一步可以擦除當前字符串中的一個迴文子串。現在問最少需要多少步才能把整個字符串擦除。
例如我們能在一步裏面擦除abcba從axbyczbea並且得到xyze。
Input
第一行是一個整數T,代表T組測試數據,對於每組測試數據輸入一個長度<=16的字符串。
Output
對於每組測試數據輸出最少的步數。
Sample Input
2
aa
abb
Sample Output
1
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   z
101000   i

000101 z^i

*/
/*
011010  ~z

100101   z
000111   i

100010 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;
}


發佈了51 篇原創文章 · 獲贊 8 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章