bzoj2121 字符串游戏(区间dp)

题目链接

题意

有一个主串$L$,和一个模式串集$S$,每次可以从主串中删除一个模式串,删除后主串剩余的两部分合并,求能得到的主串的最小长度

题解

 
  • 挺神仙的dp
  • 如果已经知道了区间$[i,j]$能否被删除的话,那么就可以用$dp[i]$表示对$L$的前$i$个字符进行操作后能得到的最小的长度,转移就很显然。
  • 所以难点就在于如何求区间能否被删除。
  • 用$f[i][j][k][l]$表示能否将区间$[i,j]$删成第$k$个模式串的前$l$个字符,$ok[i][j]$表示区间$[i,j]$能否全被删掉。从小到大枚举区间长度。
  • 转移如下:
  • 如果$L[l]=S[k][l]$,则$g[i][j][k][l]=g[i][j-1][k][l-1]$
  • 考虑删除的情况:枚举$[i,j]$的分隔点$d$
    如果$ok[d+1][j]$,则$g[i][j][k][l]|=g[i][d][k][l]$
  • $ok[i][j]|=f[i][j][k][len[k]]$
  • 这样处理完后,就可以用最开始说的dp求答案了,时间复杂度$O(|L|^3*k*l)$
查看代码
#include <bits/stdc++.h>
using namespace std;
#define _for(i,a,b) for(int i = (a);i <= (b);++i)
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
ll qpow(ll a,ll b){ll res = 1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}}
struct graph
{
    int head[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],sz;
    void init(){memset(head,-1,sizeof(head));}
    graph(){init();}
    void push(int a,int b,int c){nxt[sz]=head[a],to[sz]=b,w[sz]=c,head[a]=sz++;}
    int& operator[](const int a){return to[a];}
};
char s[153];
char t[32][22]; 
bool f[153][153][32][22];
bool ok[153][153];
int dp[153];
int len[32];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("simple.in","r",stdin);
    freopen("simple.out","w",stdout);
#endif
    int n,m;
    scanf("%s%d",s+1,&m);
    for(int i = 1;i <= m;++i)scanf("%s",t[i]+1),len[i]=strlen(t[i]+1);
    n = strlen(s+1);
    for(int i = 1;i <= n;++i){
        for(int j = 1;j <= m;++j){
            if(s[i]==t[j][1]){
                f[i][i][j][1]=1;
                if(len[j]==1)ok[i][i]=1;
            }
        }
    }
    for(int width = 2;width <= n;++width){
        for(int i = 1,j;i <= n;++i){
            j = i+width-1;
            if(j>n)break;
            for(int k = 1;k <= m;++k){
                for(int l = 1;l <= len[k];++l){
                    if(s[j]==t[k][l]){
                        f[i][j][k][l]|=f[i][j-1][k][l-1];
                    }
                    for(int p = i;p < j;++p){
                        if(ok[p+1][j])f[i][j][k][l]|=f[i][p][k][l];
                    }
                }
                ok[i][j]|=f[i][j][k][len[k]];
            }
        }
    }
    for(int i = 1;i <= n;++i){
        if(ok[1][i])dp[i]=0;
        else dp[i]=dp[i-1]+1;
        for(int j = 1;j < i;++j){
            if(ok[j+1][i])dp[i]=min(dp[i],dp[j]);
        }
    }
    cout<<dp[n]<<endl;
    return 0;
}

posted @ 2020-04-30 23:33  tryatry  阅读(116)  评论(0编辑  收藏  举报