题解:P11030 『DABOI Round 1』Blessings Repeated

节选自:DP做题记录(三)(2025.4.5 - 2025.4.19)

由于 \(m\) 小的可怜,我们考虑把 \(T\) 这个字符串拆分成若干个子串,分别放入这 \(k\) 个重复的 \(S\) 中,然后就可以用组合数计算了。

那么问题就转变成了求出 \(T\) 的所有字串在 \(S\) 中作为子序列的出现次数。我们把 \(T_{[l, r]}\) 单独抽出来,设 \(dp_{i, j}\) 表示 \(S\) 的前 \(i\) 个字符中 \(T_{l, j}\) 出现的次数,那么如果 \(S_i = T_j\),就可以把 \(dp_{i - 1, j - 1}\) 的值加进来,否则就只能赋值为 \(dp_{i - 1, j}\),因此

\[dp_{i, j} = \begin{cases} dp_{i - 1, j} + dp_{i - 1, j - 1} & S_i = T_j\\ dp_{i - 1, j} & S_i \neq T_j \end{cases} \]

其中初值为 \(dp_{0, 0} = 1\),可以发现这个式子和背包类似,因此第二维倒序枚举可以除掉 \(j\) 这一维。于是我们在时间复杂度为 \(O(nm^3)\),空间复杂度为 \(O(n)\) 的情况下求出了 \(T\) 的所有字串在 \(S\) 中作为子序列的出现次数。

现在就比较好做了,我们将 \(T\) 划分成若干个段,设划分了 \(l\) 段,那么将这 \(l\) 段放入 \(k\) 个重复的 \(S\) 中就有 \(\displaystyle\binom kl\) 种方案,而这些方案的权值是这 \(l\) 段分别在 \(S\) 中作为子序列的出现次数的积,直接加入到答案中。由于 \(m\) 只有 \(10\),这部分的时间复杂度比较小,可以忽略不计,那么我们用 \(O(nm^3)\) 的时间复杂度完成了此题。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e3 + 9, M = 19, MOD = 998244353;
int k, dp[N], tim[M][M], inv[M], fac[M], n, m, ans;
char s[N], t[M];
vector <int> vec;
int qpow(int a, int b){
	int res = 1;
	while(b > 0){
		if(b & 1)
			res = res * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return res;
}
int binom(int a, int b){
	if(b > a)
		return 0;
	__int128 res = 1;
	for(int i = a; i >= a - b + 1; i--)
		res = res * i % MOD;
	return res * inv[b] % MOD;
}
void dfs(int now){
	if(now == 0){
		int l = 1, r, tmp = 1;
		for(int i = 0; i < (int)vec.size(); i++){
			r = l + vec[i] - 1;
			tmp = tmp * tim[l][r] % MOD;
			l = r + 1;
		}
		ans = (ans + tmp * binom(k, vec.size()) % MOD) % MOD;
		return;
	}
	for(int i = 1; i <= now; i++){
		vec.push_back(i);
		dfs(now - i);
		vec.pop_back();
	}
}
signed main(){
	scanf("%lld", &k);
	scanf("%s", s + 1);
	scanf("%s", t + 1);
	n = strlen(s + 1);
	m = strlen(t + 1);
	fac[0] = 1;
	for(int i = 1; i <= m; i++)
		fac[i] = fac[i - 1] * i % MOD;
	inv[m] = qpow(fac[m], MOD - 2);
	for(int i = m - 1; i >= 0; i--)
		inv[i] = inv[i + 1] * (i + 1) % MOD;
	for(int l = 1; l <= m; l++){
		for(int r = l; r <= m; r++){
			memset(dp, 0, sizeof(dp));
			dp[0] = 1;
			for(int i = 1; i <= n; i++)
				for(int j = r; j >= l; j--)
					if(t[j] == s[i])
						dp[j - l + 1] = (dp[j - l + 1] + dp[j - l]) % MOD;
			tim[l][r] = dp[r - l + 1];
		}
	}
	dfs(m);
	printf("%lld", ans);
	return 0;
}
posted @ 2025-04-18 17:08  Orange_new  阅读(10)  评论(0)    收藏  举报