【hrbust700纪念】Hrbust 1847 擦除字符串【状压dp+Dfs枚举可行状态+剪枝】好题

本文介绍了一种算法,用于求解给定字符串中所有回文子串的最少擦除步骤。采用DFS预处理所有可能的回文子串状态,并使用DP实现状态转移,最终得出最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

思路:


1、通过Dfs枚举所有子序列状态,并判断当前枚举出来的状态是否是一个可行状态(是否是一个回文串)因为字符串并不会很长,所以时间复杂度:O(2^len*len)的时间复杂度虽然看起来很大,其实最坏也就是2^16*16==2^20.对应枚举出来的状态i如果是回文串,我们vis【i】=1.标记此种状态为回文串。


2、接下来我们设定dp【i】表示从状态0添加回文串到状态i需要的步数。虽然我们问题上说的是让我们从一个字符串去擦除回文串需要的最少步数,而因为位运算操作之类的方便角度出发,我们其实可以将擦除回文串变成增加回文串。


3、那么dp【0】=0;ans=dp【(1<<len)-1】;其状态转移方程:dp【q】=min(dp【q】,dp【i】+1);当然,q是i在加了某个回文串之后的状态。有了状态转移方程,分析其时间复杂度,如果i,q两个状态都通过直接枚举的话,是会达到:O(1<<len*1<<len)的时间复杂度,最坏也达到了2^16*2^16==2^32显然会超时。那么我们做简单优化.


4、如果当前枚举到的i状态:首先我们将i状态按位取反得到状态tmp,然后我们判断一下这个tmp是不是一个回文串,如果是(vis【i】==1),那么状态q==tmp|i,然后tmp--。那么是不是优化到这里我们就完事大吉了呢?显然优化的还不够彻底,我们继续优化、对应状态tmp,判断过后,我们明显需要tmp--,对下一个状态进行判断,那么对应如果下一个tmp的某一位有1,而状态i也有1,显然这种情况是不需要的(重叠上了字符串,明显tmp现在非法),而且如果这个1所在位为最高位,那么将会枚举了(1<<(len-1))这些不需要枚举的情况,那么我们可以进行优化:tmp--之后,tmp&x.令其中不需要出现的1去掉、这样就能做到极大的优化。


5、思路建立的差不多过后,我们剩下的工作就是写代码啦。细节处理部分可以参考代码;


Ac代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int n;
int dp[1<<17];
int vis[1<<17];
char a[50];
void Dfs(int cur,int tmp[],int sum)
{
    if(cur==n)
    {
        char aa[17];
        int cont=0;
        for(int i=0;i<n;i++)
        {
            if(tmp[i]==1)
            {
                aa[cont++]=a[i];
            }
        }
        int flag=0;
        for(int i=0;i<cont/2;i++)
        {

            if(aa[i]==aa[cont-i-1])continue;
            else
            {
                flag=1;
                break;
            }
        }
        if(flag==0)vis[sum]=1;
        return ;
    }
    for(int i=0;i<2;i++)
    {
        tmp[cur]=i;
        Dfs(cur+1,tmp,sum+i*(1<<(n-cur-1)));
    }
}
void getmap()
{
    memset(vis,0,sizeof(vis));
    n=strlen(a);
    int tmp[20];
    Dfs(0,tmp,0);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",a);
        getmap();
        memset(dp,0x3f3f3f3f,sizeof(dp));
        int end=1<<n;
        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(vis[j]==0)continue;
                int q=i|j;
                dp[q]=min(dp[q],dp[i]+1);
            }
        }
        printf("%d\n",dp[end-1]);
    }
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值