非常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;
}
}