【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;
}
posted @ 2019-08-13 19:29  EZ_WYC  阅读(123)  评论(0编辑  收藏  举报