【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} imes 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
", (sum[n-1] + mod) % mod);
	
	return 0;
}
原文地址:https://www.cnblogs.com/2016gdgzoi509/p/11347978.html