BZOJ2121: 字符串游戏

138 篇文章 0 订阅

区间DP
用C[l][r]表示l~r是否能被全部删掉,f[l][r][k][x]表示l~r和第k个串匹配是否能匹配到第x位,
注意到小串的长度≤21,而且f是个bool变量,所以可以把x压成int,
而推一下f的转移方程发现对于不同的l,他们之间是互不影响的,所以可以把l这一维删掉
那么枚举左端点,f[r][k]就代表了l~r,和第k个串的匹配情况,而且压位下转移十分方便,转移后要尝试跳一些可以完全删去的区间转移
当f[r][k],第length[k]位为1(即可以完全匹配)时C[l][r]=true

最后求出了所有C,再用一个DP求最多删去多少
code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void up(int &x,const int &y){if(x<y)x=y;}
const int maxn = 35;
const int maxm = 155;
const int maxl = 25;

char str[maxm],s[maxn][maxl];
int leng[maxn];
int len,n,f[maxm][maxn],mat[maxm][maxn];
bool C[maxm][maxm];
int ans[maxm];

int main()
{
    scanf("%s",str+1); len=strlen(str+1);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1),leng[i]=strlen(s[i]+1);

    for(int i=1;i<=len;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=leng[j];k++) if(str[i]==s[j][k]) mat[i][j]|=(1<<k);

    for(int l=len;l>=1;l--)
    {
        memset(f,0,sizeof f);
        for(int k=1;k<=n;k++) f[l-1][k]=1;
        for(int r=l;r<=len;r++)
        {
            for(int k=l;k<r;k++) if(C[l][k]&&C[k+1][r]) {C[l][r]=true; break;}
            for(int k=1;k<=n;k++)
            {
                f[r][k]|=(f[r-1][k]<<1)&mat[r][k];
                for(int x=r+1;x<=len;x++)
                    if(C[r+1][x]) f[x][k]|=f[r][k];
                if(f[r][k]&(1<<leng[k])) C[l][r]=true;
            }
        }
    }

    memset(ans,0,sizeof ans);
    for(int i=1;i<=len;i++)
    {
        ans[i]=ans[i-1];
        for(int j=1;j<=i;j++)if(C[j][i]) up(ans[i],ans[j-1]+i-j+1);
    }
    printf("%d\n",len-ans[len]);

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值