Description
从字符串 s s s中选出子集从左往右按顺序放到 p p p的结尾( p p p初始为空串),最多可以这样操作两次,询问是否能得到 p = t ( 1 ≤ ∣ t ∣ ≤ ∣ s ∣ ≤ 400 ) p=t \ (1\leq|t|\leq|s|\leq400) p=t (1≤∣t∣≤∣s∣≤400)
Solution
在
t
t
t中枚举断点
m
i
d
mid
mid,
t
[
1
,
m
i
d
]
t[1, mid]
t[1,mid]为第一次操作选出,
t
[
m
i
d
+
1
,
∣
t
∣
]
t[mid + 1, |t|]
t[mid+1,∣t∣]为第二次操作选出
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示当前是否能达到第一次操作串匹配
i
i
i位,第二次操作串中匹配
j
j
j位
f
[
i
]
[
j
]
∣
=
{
f
[
i
−
1
]
[
j
]
(
s
[
k
]
=
=
t
[
i
]
)
f
[
i
]
[
j
−
1
]
(
s
[
k
]
=
=
t
[
j
+
m
i
d
]
)
f[i][j] \ \ |= \begin{cases} f[i - 1][j] \ (s[k] == t[i]) \\ f[i][j - 1] \ (s[k]==t[j + mid])\end{cases}
f[i][j] ∣={f[i−1][j] (s[k]==t[i])f[i][j−1] (s[k]==t[j+mid])
这样复杂度
O
(
n
4
)
O(n^4)
O(n4)
显然有这样一个结论
若
f
[
a
]
[
b
]
=
=
t
r
u
e
f[a][b] == true
f[a][b]==true,
∀
x
∈
[
1
,
a
]
,
y
∈
[
1
,
b
]
\forall x \in [1,a],y \in [1,b]
∀x∈[1,a],y∈[1,b], 有
f
[
x
]
[
y
]
=
=
t
r
u
e
f[x][y]==true
f[x][y]==true
因此
i
,
j
i,j
i,j均不为当前最长匹配的转移是多余的
M
a
x
1
[
i
]
Max1[i]
Max1[i]表示第一次操作串中匹配
i
i
i位时,第二次操作串中最多匹配几位
M
a
x
2
[
i
]
Max2[i]
Max2[i]表示第二次操作串中匹配
i
i
i位时,第一次操作串中最多匹配几位
每次只需拓展这两个数组就行
两数组相反,每次拓展完要通过自己修改对方
注意
s
s
s中每个字符只能用一次,要倒序枚举,互相修改要等两数组自身都修改完
复杂度
O
(
n
3
)
O(n^3)
O(n3)
#include <bits/stdc++.h>
using namespace std;
int T, len1, len2, Max1[410], Max2[410];
char s[410], t[410];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%s\n%s", s + 1, t + 1);
len1 = strlen(s + 1), len2 = strlen(t + 1);
bool flag = false;
for (int Mid = 1; Mid <= len2; Mid++) {
for (int i = 1; i <= Mid; i++) Max1[i] = -1;
for (int j = 1; j + Mid <= len2; j++) Max2[j] = -1;
Max1[0] = Max2[0] = 0;
for (int k = 1; k <= len1; k++) {
for (int i = Mid; i; i--) if (s[k] == t[i]) Max1[i] = max(Max1[i], Max1[i - 1]);
for (int j = len2 - Mid; j; j--) if (s[k] == t[j + Mid]) Max2[j] = max(Max2[j], Max2[j - 1]);
for (int i = 0; i <= Mid && Max1[i] != -1; i++) Max2[Max1[i]] = max(Max2[Max1[i]], i);
for (int j = 0; j + Mid <= len2 && Max2[j] != -1; j++) Max1[Max2[j]] = max(Max1[Max2[j]], j);
}
if (Max1[Mid] == len2 - Mid) {
flag = true;
break;
}
}
puts(flag ? "YES" : "NO");
}
return 0;
}