P11030 『DABOI Round 1』Blessings Repeated 题解
首先我们注意到 \(\lvert T \rvert\) 很小,所以可以考虑将 \(T\) 拆分为若干个段,并钦定每一段都只被同一个 \(S\) 包含。那么我们需要先处理出对于 \(T\) 的每一个子段,在 \(S\) 中作为子序列出现的次数。这个可以DP来做,我们设 \(f_{i,l,r}\) 表示 \(S\) 的前 \(i\) 位中有多少个子序列和 \(T\) 的第 \(l\) 到 \(r\) 位相同。转移也非常简单:
\[f_{i,l,r}=
\begin{aligned}
\begin{equation}
\begin{cases}
f_{i-1,l,r}&(s_i\ne t_r) \\
f_{i-1,l,r}+1&(s_i=t_r,l=r)\\
f_{i-1,l,r}+f_{i-1,l,r-1}&(s_i=t_r,l\ne r)
\end{cases}
\end{equation}
\end{aligned}
\]
现在我们考虑统计答案,直接dfs就可以了,当我们搜索出一种拆分将 \(T\) 拆成了 \(p\) 段,第 \(i\) 段从 \(l_i\) 到 \(r_i\) ,那么对答案的贡献应该是:
\[{k\choose m}\prod_{i=1}^m f_{n,l_i,r_i}
\]
code:
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e3+10,mod=998244353;
ll f[N][12][12],S,T,inv[12],ans,k;
char s[N],t[12];
ll qp(ll x,ll y)
{
ll res=1;
while(y)
{
if(y&1) (res*=x)%=mod;
(x*=x)%=mod,y>>=1;
}
return res;
}
ll C(ll x)
{
ll res=1;
for(int i=0;i<x;i++) (res*=(k-i)%mod)%=mod;
return res*inv[x]%mod;
}
void dfs(int l,int pos,ll res)
{
if(l>T) return (ans+=res*C(pos))%=mod,void();
for(int r=l;r<=T;r++) dfs(r+1,pos+1,res*f[S][l][r]%mod);
}
int main()
{
cin>>k;inv[1]=1;
for(int i=2;i<=10;i++) inv[i]=inv[i-1]*qp(i,mod-2)%mod;
scanf("%s%s",s+1,t+1);
S=strlen(s+1),T=strlen(t+1);
for(int i=1;i<=S;i++)
for(int l=1;l<=T;l++)
for(int r=l;r<=T;r++)
{
f[i][l][r]=f[i-1][l][r];
if(s[i]==t[r]) (f[i][l][r]+=(l==r?1:f[i-1][l][r-1]))%=mod;
}
dfs(1,0,1);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号