【XSY2472】string
题目
Description
输入文件: string.in
输出文件: string.out
给定一个由且仅由字符 'H' , 'T' 构成的字符串 \(S\) .
给定一个最初为空的字符串 \(T\) , 每次随机地在 T 的末尾添加 'H' 或者 'T' .
问当 \(S\) 为 \(T\) 的后缀时, 在末尾添加字符的期望次数.
Input
输入只有一行, 一个字符串 \(S\) .
Output
输出只有一行, 一个数表示答案.
为了防止运算越界, 你只用将答案对 \(10^9+7\) 取模.
题解
题目可以理解成不断向\(T\)添加字符, 直到\(S\)匹配上\(T\)\((\)\(S\)为模式串\()\), 的期望长度。
于是我们可以预处理出失配指针\(f_i\)
我们可以设\(dp_i\)表示现在已匹配到\(S_i\), 在匹配上\(S_{i+1}\)的期望次数。
于是我们可以分类讨论一下。
如果这一次直接匹配上\(S_{i+1}\), 那么这一部分的贡献为\(\frac{1}{2} \times 1 = \frac{1}{2}\)
如果这一次匹配失败跳了失配指针, 贡献为匹配上\(S_{f_i,\;i+1}\)的期望次数\((\)参考KMP匹配过程\()\), 也就是\(\frac{1}{2}(\sum_{k = f_i}^i dp_k) + \frac{1}{2}\),\((\)加\(\frac{1}{2}\)是因为要匹配上\(S_{i+1}\)\()\)
于是\(dp_i = \frac{1}{2} + \frac{1}{2} + \frac{1}{2}(\sum_{k = f_i}^i dp_k)\)
注意到等号两边都出现了\(dp_i\), 于是我们可以把上面的式子当成一个方程, 解出来是\(dp_i = 2 + \sum_{k = f_i}^{i-1} dp_k\), 我们用前缀和优化一下, 单次转移就变成\(O(1)\)了。
代码
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod = 1e9 + 7;
const int N = 1000010;
char S[N];
int f[N], nxt[N][2];
int sum[N], dp[N];
int main()
{
scanf("%s", S + 1);
int n = strlen(S + 1);
f[1] = 0;
int j = 0;
for (int i = 2; i <= n; i++)
{
while (j && S[i] != S[j+1]) j = f[j];
if (S[i] == S[j+1]) j++;
f[i] = j;
}
for (int i = 0; i < n; i++)
if (S[i+1] == 'H')
nxt[i][0] = i + 1, nxt[i][1] = nxt[f[i]][1];
else
nxt[i][0] = nxt[f[i]][0], nxt[i][1] = i + 1;
sum[0] = f[0] = 2;
for (int i = 1; i < n; i++)
{
int g;
if (S[i+1] == 'H') g = nxt[i][1]; else g = nxt[i][0];
dp[i] = ((sum[i-1] - (g ? sum[g-1] : 0) + 2) % mod + mod) % mod;
sum[i] = (sum[i-1] + dp[i]) % mod;
}
printf("%d\n", (sum[n-1] + mod) % mod);
return 0;
}