Codeforces 1303E Erase Subsequences

Description

描述

给定两个字符串 $s$ 和 $t$。问能否找到 $s$ 的两个不相交的子序列(可以为空) $s_1,s_2$ 使 $t=s_1s_2$。

输入

第一行为一个正整数 $T$($1 le T le 100$)表示数据组数。

接下来每组数据为两个字符串 $s$ 和 $t$($1le|t|le|s|le 400$)。

输出

对于每组数据,一个 YESNO 表示是否可行。

样例

输入

4
ababcd
abcba
a
b
defi
fed
xyz
x

输出

YES
NO
NO
YES

Solution

本题解字符串下标从 $1$ 开始。

看到 $1 le |t| le |s| le 400$,盲猜可能是的三次方的做法。

根据题意,第一次操作一定是创造了 $t$ 的前半部分,第二次操作一定是创造了 $t$ 的后半部分。于是我们枚举一下这个断点。用 $t_1$ 来表示 $t_{1cdots len_l}$(前一半),$t_2$ 表示 $t_{|t| - len_r+1 cdots |t|}$(后一半),其中 $len_l = |t| - len_r$,也就是 $len_l + len_r = |t|$。

用 $a_{i, j}$ 表示 $s_{i cdots |s|}$ 中,第一个 $j$ 的位置。也就是 $forall i le k < a_{i,j}, s_k eq j$,同时 $s_{a_{i,j}} = j$。如果 $forall i le k le |s|, s_i e j$ ,那么有 $a_{i,j} = infty$。

考虑 DP,用 $f_{i, j}$ 表示至少需要 $s_1 sim s_{f_{i,j}}$,才能匹配 $t_{1 1 cdots i}$ 和 $t_{2 1 cdots j}$。转移当然是考虑这次匹配 $t_{1 i}$ 还是匹配 $t_{2 j}$,如果是前者,$f_{i,j} = a_{f_{i - 1, j} + 1, t_{1 i}}$(也就是 $f_{i - 1, j}$ 后面第一个 $t_{1 i}$ 的位置);如果是后者,就有 $f_{i,j} = a_{f_{i, j - 1} + 1, t_{2 j}}$。两者取个 $min$ 即可。最终的成功与否取决于 $f_{len_l, len_r}$ 是否等于 $infty$。如果 $a$ 已经求出来了,这部分将是 $O(|t|^2)$ 的。但是外层还要枚举断点,所以其实是 $O(|t|^3)$。

至于 $a$ 的话,可以从 $|s| o 1$ 更新,首先把 $a_{i}$ 复制为 $a_{i + 1}$,然后把 $a_{i, s_i}$ 更新为 $i$ 即可。这个是 $O(Sigma cdot |s|)$ ($Sigma$ 表示字符集大小)的,常数比较优秀。当然也可以按照定义写一个 $O(Sigma cdot |s|^2)$ 的,不影响时间复杂度。

#include <bits/stdc++.h>
using namespace std;
const int N = 405;
int f[N][N], a[N][30];
const int INF = 0x3f3f3f3f;
int main()
{
	ios::sync_with_stdio(false);
	int t;
	cin >> t;
	while(t--)
	{
		string s, t;
		cin >> s >> t;
		int ls = s.size(), lt = t.size();
		s = " " + s;
		t = " " + t;
		bool ok = false;
		for(int lenl = 1; lenl <= lt; lenl++)
		{
			string t1 = " " + t.substr(1, lenl);
			string t2 = " " + t.substr(lenl + 1, lt - lenl);
			memset(f, 0, sizeof f);
			f[0][0] = true;
			memset(a[ls + 1], 0x3f, sizeof a[ls + 1]);
			for(int i = ls; i; i--)
			{
				memcpy(a[i], a[i + 1], sizeof a[i]);
				a[i][s[i] - 'a'] = i;
			}
			int lenr = lt - lenl;
			memset(f, 0x3f, sizeof f);
			f[0][0] = 0;
			for(int i = 0; i <= lenl; i++)
			{
				for(int j = 0; j <= lenr; j++)
				{
					if(i && f[i - 1][j] != INF)
						f[i][j] = min(f[i][j], a[f[i - 1][j] + 1][t1[i] - 'a']);
					if(j && f[i][j - 1] != INF)
						f[i][j] = min(f[i][j], a[f[i][j - 1] + 1][t2[j] - 'a']);
				}
			}
			if(f[lenl][lenr] != INF)
			{
				ok = true;
				break;
			}
		}
		cout << (ok ? "YES" : "NO") << endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/syksykCCC/p/CF1303E.html