Educational Codeforces Round 89 (Rated for Div. 2) G. Construct the String(dp+括号匹配思想)

题目

给一个仅有小写字母和点(删除符)构成的串s

和另一个只有小写字母构成的串t,(1<=|t|<=|s|<=1e4)

定义对s使用f函数时,执行如下功能:

构造一个空串r,从左到右遍历s,

如果是小写字母就追加到r末尾,否则删掉r末尾的字符

扫描完之后,返回这个串r

求删去最少的字符数,使得f(s)=t

思路来源

官方题解

题解

显然,会想到dp[i][j]表示只考虑s的前i个字符匹配到t的前j个字符时的最小代价,

如果删掉字符s[i],dp[i-1][j]向dp[i][j]转移

如果不删掉,分为两种情况:

①是字母,dp[i-1][j]向dp[i][j+(s[i]==t[j]]转移,即相等时可以多匹配一个

②是删除符,dp[i-1][j]向dp[i][j-1]转移,执行删除功能

然后,就发现过不了样例了……

c..code..c...o.d.de
code
3

这个样例还是很良心的,

它说明,当前在匹配到co的时候,coc也是可以存在的,只是c需要用后面的删除符来删除

但是,由于保留状态的局限性,我们没办法保留coc串,因为其与t串前缀不相等

于是题解告诉我们,如果c需要用后面的删除符删除时,用最近的可以把c这一段删掉的删除符就可以了

具体实现时,考虑把字母当成(,把删除符当成),即找到字母后面第一个)满足括号匹配的位置

加上这一种情况就可以了,1e4*1e4的时空复杂度是认真的么2333

 

题解还大概证明了一下,为什么这样做,不会少讨论情况

首先,c花费1代价删和不删都讨论过了,此时,只能用原序列删除符删

用原序列删除符删的时候,第一个括号匹配的位置肯定不会比第二个括号匹配位置差,

因为后面那段还可以再dp一下,最优子结构

中间如果跳过一些字母,则这些字母需要额外代价删,跳过删除符同理,不跳过是不耗代价的

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define sci(a) scanf("%d",&(a))
const int N=1e4+10,INF=0x3f3f3f3f;
int dp[N][N],to[N],n,m;
char s[N],t[N];
int main(){
    scanf("%s%s",s+1,t+1);
    n=strlen(s+1);m=strlen(t+1);
    rep(i,1,n){
        if(s[i]=='.'){
            to[i]=-1;
            continue;
        }
        int cnt=1;
        rep(j,i+1,n){
            if(s[j]=='.')cnt--;
            else cnt++;
            if(cnt==0){
                to[i]=j;//[i,j]是最短的合法括号序列 其中字母为( .为右
                break;
            }
        }
    }
    memset(dp,INF,sizeof dp);
    dp[0][0]=0;
    rep(i,0,n-1){
        rep(j,0,m){//删掉这位
            dp[i+1][j]=min(dp[i+1][j],dp[i][j]+1);
            if(s[i+1]=='.' && j){
                dp[i+1][j-1]=min(dp[i+1][j-1],dp[i][j]);
            }
            else if(j+1<=m && s[i+1]==t[j+1]){
                dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]);
            }
            if(~to[i+1]){//字母区间[i+1,to[i+1]]
                dp[to[i+1]][j]=min(dp[to[i+1]][j],dp[i][j]);
            }
        }
    }
    printf("%d\n",dp[n][m]);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值