OI集训 Day21
Content:字符串算法进阶
Date:2025.8.6
Luogu-P13270 最小表示法
题意
给定一个长度为 \(n\) 的字符串 \(s\),求 \(s\) 的最小表示法。
思路
首先如果暴力的话,复杂度是 \(O(n^2)\) 的,但是我们发现如果我们当前已经发现了一个最小表示法,我们想要找到一个比这个还小的表示法,那么一定存在一个位置,使得这个位置上的字符不相等,根据这个就可以把复杂度优化到 \(O(n)\)。
提交记录:Link。
Luogu-P5357 AC自动机
题意
给定一个字符串 \(s\) 和 \(n\) 个模式串 \(t_i\),求每个模式串在字符串 \(s\) 中的出现次数。
思路
首先我们可以对 \(s\) 建一个 AC 自动机,然后统计每个模式串在这个 AC 自动机的字典树上经过了那些节点,然后打标记。再建出 Fail 树,然后在 Fail 树上对标记求和。
提交记录:Link。
模拟题 - 嗑瓜子
题意
有一个人在嗑 \(n\) 个瓜子,每次在瓜子中等概率拿出一个,有以下两种情况:
- 如果拿出的是瓜子,则吃掉,并把壳扔回瓜子中。
- 如果拿出的是壳,则将其丢弃。
求他吃掉所有瓜子的期望。
思路
定义 \(dp_{i,j}\) 表示有 \(i\) 个瓜子和 \(j\) 个壳的期望。根据题意写出转移即可。
Important:注意空间,因为 \(j_{max}\) 可以达到 \(2 \times n\),所以第二维要开到两倍。
Code
#include <iostream>
#define OnlineJudge
using std::cin;
using std::cout;
constexpr int MOD = 998244353;
constexpr int N = 5005; // ! 注意两倍空间
long long dp[N][N];
long long QuickPow(long long base, long long pow) {
long long result = 1;
while (pow) {
if (pow & 1) result = result * base % MOD;
base = base * base % MOD;
pow >>= 1;
}
return result;
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n;
cin >> n;
dp[n][0] = 1;
for (int i = n - 1; i >= 0; i--) {
for (int j = (n - i) * 2; j >= (i == 0 ? 2 : 0); j--) {
if (i == 0) {
dp[i][j] = QuickPow(j - 1, MOD - 2) * dp[i + 1][j - 2] % MOD;
} else {
if (j < 2) {
dp[i][j] = (j + 1) * QuickPow(i + j + 1, MOD - 2) % MOD * dp[i][j + 1] % MOD;
} else if (j == (n - i) * 2) {
dp[i][j] = (i + 1) * QuickPow(i + j - 1, MOD - 2) % MOD * dp[i + 1][j - 2] % MOD;
} else {
dp[i][j] = ((i + 1) * QuickPow(i + j - 1, MOD - 2) % MOD * dp[i + 1][j - 2] % MOD + (j + 1) * QuickPow(i + j + 1, MOD - 2) % MOD * dp[i][j + 1] % MOD) % MOD;
}
}
}
}
#ifndef OnlineJudge
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= 2 * (n - i); j++) {
cout << dp[i][j] << ' ';
}
cout << '\n';
}
#endif
long long answer = 0;
for (int i = 1; i <= n * 2; i++) {
answer = (answer + (3 * n - i) * dp[0][i] % MOD) % MOD;
}
cout << answer << '\n';
return 0;
}
本文来自博客园,作者:Fallen_Leaf,转载请注明原文链接:https://www.cnblogs.com/FallenLeaf/p/19025871