非常nice的dp题。
状态是这样的:
另
t
=
a
+
b
t=a+b
t=a+b
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示a字符串填了前
i
i
i个,
b
b
b字符串填了前
j
j
j个需要的最小
∣
s
∣
|s|
∣s∣长度。
d
p
[
0
]
[
0
]
dp[0][0]
dp[0][0]=0
当然我们还需要枚举分界线,即我们想要的
∣
a
∣
|a|
∣a∣,如此才可以确定
∣
b
∣
|b|
∣b∣的前
j
j
j个字符到底是什么。
此外,为了
状态转移方程:
i f ( s [ l e n s ] = = b [ l e n a ] ) d p [ l e n a + 1 ] [ l e n b ] = m i n ( d p [ l e n a + 1 , l e n b ] , d p [ l e n a ] [ l e n b ] 转 移 到 新 状 态 的 下 一 个 ∣ S ∣ 长 度 ) if(s[lens]==b[lena])\\ dp[lena+1][lenb]=min(dp[lena+1,lenb],dp[lena][lenb]转移到新状态的下一个|S|长度) if(s[lens]==b[lena])dp[lena+1][lenb]=min(dp[lena+1,lenb],dp[lena][lenb]转移到新状态的下一个∣S∣长度)
对于这个更新状态的
∣
S
∣
|S|
∣S∣长度,我们需要利用当前状态的最小长度
(
x
)
(x)
(x)来找。
显然是贪心地选取
s
x
,
s
x
+
1
,
⋯
(
0
−
i
n
d
e
x
e
d
)
s_x,s_{x+1},\cdots (0-indexed)
sx,sx+1,⋯(0−indexed)中出现的第一个匹配字符。
预处理出一个
n
x
t
[
p
o
s
]
[
j
]
nxt[pos][j]
nxt[pos][j]表明
s
x
,
s
x
+
1
,
⋯
(
0
−
i
n
d
e
x
e
d
)
s_x,s_{x+1},\cdots (0-indexed)
sx,sx+1,⋯(0−indexed)中出现的第一个匹配字符
j
j
j。
l
e
n
b
lenb
lenb更新同理
复杂度为
O
(
n
)
∗
O
(
n
2
)
O(n)*O(n^2)
O(n)∗O(n2)枚举断点和双重状态
传送门
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 402
#define fore(n) for(ll i=1;i<=n;i++)
ll n,m;
int dp[MAXN][MAXN];
int nxt[MAXN][30];
int pos[30];
int main()
{
int t;
// freopen("E://tt.txt","r",stdin);
cin>>t;
while(t--)
{
string a;
string b;
cin>>a>>b;
n=a.length();
m=b.length();
for(int i=0;i<26;i++)
pos[i]=n+1;
for(int i=n+1;i>=0;i--)
{
for(int j=0;j<26;j++)
{
nxt[i][j]=pos[j];
}
if(i&&i<=n)pos[a[i-1]-'a']=i;
}
int is=0;
for(int x=0;x<=m;x++)
{
for(int i=0;i<=m;i++)
for(int j=0;j<=m;j++)
dp[i][j]=n+1;
dp[0][0]=0;
for(int i=0;i<=x;i++)
for(int j=0;j<=m-x;j++)
{
if(i<m)
dp[i+1][j]=min(dp[i+1][j],nxt[dp[i][j]][b[i]-'a']);
if(j+x<m)
dp[i][j+1]=min(dp[i][j+1],nxt[dp[i][j]][b[j+x]-'a']);
}
if(dp[x][m-x]<=n)
is=1;
}
cout<<(is?"YES":"NO")<<endl;
}
}