题目
给一个仅有小写字母和点(删除符)构成的串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;
}