• 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;
    }
  • 相关阅读:
    scheme资料
    lisp 资料
    linux input 文章
    qt 键盘驱动分析
    表达式模板 (C++学习)
    qt 键盘插件(mine)
    qt 私有实现导致plugin 加载 提示 undefined symbol
    Visual C++ 8.0对象布局的奥秘:虚函数、多继承、虚拟继承
    linux内核input子系统解析
    qt 键盘 插件
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1303E.html
Copyright © 2020-2023  润新知