E. Erase Subsequences(dp)

题目:http://codeforces.com/contest/1303/problem/E
题意:给定字符串 s s s t t t,将 s s s抽取出一个子串(可以不连续),得到串 p 1 p_1 p1,再将剩下的字符串 s 2 s_2 s2抽取一个子串 p 2 p_2 p2(可以为空), p 1 p_1 p1 p 2 p_2 p2连接,问能否得到字符串t。说白了就是能否从s种找到两个不重叠的子串 p 1 p_1 p1 p 2 p_2 p2,使得 p 1 + p 2 = t p_1+p_2=t p1+p2=t其中 1 < = l e n g t h ( s ) , l e n g t h ( t ) < = 400 1<=length(s),length(t)<=400 1<=length(s),length(t)<=400
参考:http://codeforces.com/blog/entry/73872
参考2:https://www.bilibili.com/video/av88620060?p=5
题解:将字符串 t t t拆分为 t 1 + t 2 t1+t2 t1+t2定义 d p [ i ] [ j ] dp[i][j] dp[i][j]表示凑成 t 1 t1 t1的前 i i i个和 t 2 t2 t2的前 j j j个需要的 s s s的最小长度。 d p [ i ] [ j ] dp[i][j] dp[i][j] d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j] d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1]转移,每次寻找下一个和 t 1 [ i ] / t 2 [ j ] t1[i]/t2[j] t1[i]/t2[j]匹配的最小的s的位置,需要预处理s的每一个字符的最小位置 n x t [ ] [ 26 ] nxt[][26] nxt[][26]

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 410;

string s,t;
int n,m;
int nxt[maxn][30];
int dp[maxn][maxn];
void nxt_init() {
	for(int i = 0;i <= 25;i++) nxt[n][i] = n+1;
	for(int i = n;i >= 1;i--) {
		for(int j = 0;j <= 25;j++) 
			nxt[i-1][j] = nxt[i][j];
		nxt[i-1][s[i-1]-'a'] = i;
	}
}
bool judge(string t1,string t2) {
	int len1 = t1.length(),len2 = t2.length();
	dp[0][0] = 0;
	for(int i = 0;i <= len1;i++) {
		for(int j = 0;j <= len2;j++) {
			if(!i && !j) continue;
			dp[i][j] = n+1;
			if(i && dp[i-1][j] < n) dp[i][j] = min(dp[i][j],nxt[dp[i-1][j]][t1[i-1]-'a']);
			if(j && dp[i][j-1] < n) dp[i][j] = min(dp[i][j],nxt[dp[i][j-1]][t2[j-1]-'a']);
		}
	}
	return dp[len1][len2]<=n;
}
int main() {
	int T;cin>>T;
	while(T--) {
		cin>>s>>t;
		bool flag = 0;
		m = t.length();
		n = s.length();
		nxt_init(); 
		for(int i = 0;i < m;i++) {
			if(judge(t.substr(0,i),t.substr(i))) {
				flag = 1;break;
			}
		}
		if(flag) puts("YES");
		else puts("NO");
	}	
} 

除了上述做法,网上也看到了另一种dp方法,不需要借用额外空间。
定义 d p [ i ] [ j ] dp[i][j] dp[i][j]表示用了s的前i个和t1的前j个能得到的最大的t2的长度。转移时拿 s [ i − 1 ] s[i-1] s[i1] t 1 [ j − 1 ] t1[j-1] t1[j1] t 2 [ d p [ i − 1 ] [ j ] ] t2[dp[i-1][j]] t2[dp[i1][j]]比较。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 410;

string s,t;
int n,m;
int dp[maxn][maxn];

bool judge(string t1,string t2) {
	int len1 = t1.length(),len2 = t2.length();
	memset(dp,-1,sizeof(dp)); 
	dp[0][0] = 0;
	for(int i = 0;i <= n;i++) {
		for(int j = 0;j <= len1;j++) {
			if(i) dp[i][j] = max(dp[i-1][j],dp[i][j]);
			if(i && j && dp[i-1][j-1] != -1 && s[i-1] == t1[j-1]) 
				dp[i][j] = max(dp[i-1][j-1],dp[i][j]);
			if(i && dp[i-1][j] != -1 && t2[dp[i-1][j]] == s[i-1]) 
				dp[i][j] = max(dp[i][j],dp[i-1][j]+1);
		}
	}
	return dp[n][len1]>=len2;
}
int main() {
	int T;cin>>T;
	while(T--) {
		cin>>s>>t;
		bool flag = 0;
		m = t.length();
		n = s.length();
		for(int i = 0;i < m;i++) {
			if(judge(t.substr(0,i),t.substr(i))) {
				flag = 1;break;
			}
		}
		if(flag) puts("YES");
		else puts("NO");
	}	
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值