比较模板的题。
题目
给定一个字符串 (S),(n) 个字符串 (T_1,T_2,dots,T_n),每个串长度都是 (m),一个长度为 (k) 的有理数序列 (p_1,p_2,dots,p_k) 保证 (sum p_i=1)。每个字符串由前 (k) 个小写字母构成。
我们进行下面的过程:
- 如果存在 (j(1le jle n)) 满足 (T_j) 是 (S) 的子串,那么停止这个过程。
- 否则按 (p_i) 的概率将第 (i) 个小写字母加到 (S) 的末尾,然后继续这个过程。
定义 (f(S,T,p)) 表示停止的时候 (S) 的期望长度。
给一个字符串 (R),对 (R) 的每个前缀,求出答案。也就是求出 (f(R[1dots i],T,p)) 对 (iin [1,|R|]) 的所有值。
(nle 100,nmle 10000,|R|le 10^4),答案对 (1 000 000 007 取模)
题解:
一个显然的做法是建出 (T_1,T_2,cdots,T_n) 的 AC 自动机,则题目相当于在 AC 自动机上随机游走,问走到叶结点之一的期望次数。
设 (E(x_i)) 表示从 AC 自动机上第 (i) 个点出发,期望多少次能走到一个叶结点,列方程后高斯消元即得答案。未知数个数达到 (O(nm)),时间复杂度 (O(n^3m^3)),过不了。
考虑优化未知数个数。观察方程 (E(x_u)=1+sum_{c=1}^k p_c E(x_{operatorname{next}_{u,c}}))。
((operatorname{next}_{u,c}) 表示从点 (u) 走 (c) 的转移边到的结点)中,(operatorname{next}_{u,c}) 要么是 Trie 树上 (u) 的儿子,要么深度不超过 (u) 的深度。
考虑树剖:对每个点 (u) 任选一个儿子 (v) 作为偏爱儿子进行树链剖分。那这个方程就可以看作把 (v) 用 (u) 的其他儿子和深度不超过 (u) 的深度的结点表示出来。
那么我们发现经过代入,每个结点的答案都可以通过一些链顶结点的答案来表示。怎么做呢?我们按从浅到深的顺序来做。注意到对一个方程 (E(x_u)=1+sum_{c=1}^k p_c E(x_{operatorname{next}_{u,c}})),它所涉及到的最深层的结点是 (u) 的全部儿子,而它们除了偏爱儿子 (v) 以外全是链顶结点。而对于所涉及到的深度不超过 (u) 的深度的结点,由于我们是按从浅到深的顺序,所以一定已经被表出,代入即可。
由于叶子结点不超过 (n) 个,所以剖出链的个数 (O(n)),未知数个数 (O(n)),总时间复杂度 (O(n^3+n^2mk+|R|)),可以通过。
此外这题还可以使用概率生成函数。
不熟悉概率生成函数的同学可以先做做 歌唱王国 和 硬币游戏。
首先把输入的 (T_1,T_2,cdots,T_n) 拿去去重。
先考虑 (S) 初始为空的情况怎么做。
记 (F_i(x)=sum_{jge 0} f_{i,j}x^j,G(x)=sum_{jge 0} g_jx^j),其中 (f_{i,j}) 表示随机第 (j) 次时恰好跟 (T_i) 匹配上的概率,(g_j) 表示随机第 (j) 次时还没有结束的概率。
根据概率生成函数的基础知识,我们要求的是 ((sum F_i)'(1))。
由于 (g_{j-1}-g_{j}=sum_i f_{i,j}),于是有 (1+xG(x)=G(x)+sum_i F_i(x))。
此外我们根据 (n) 个串的信息还能列出 (n) 个方程。具体来说,若当前还没有结束随机过程,这时我们在串尾接上 (T_i=T_{i,1}T_{i,2}cdots T_{i,m}) 后,随机过程必然是已经结束了。然而也可能还没有接完 (T_i) 就已经匹配完了。总之:(G(x)prod_{k=1}^m(p_{T_{i,k}}x)=sum_{j=1}^n F_j(x)sum_{l|T_i[1:l]=T_j[m-l+1:m]}prod_{k=l+1}^m(p_{T_{i,k}}x))。
我们最终要求 ((sum F_i)'(1))。由第一个式子:
((sum F_i)(x)=1+(x-1)G(x))
((sum F_i)'(x)=G(x)+(x-1)G'(x))
((sum F_i)'(1)=G(1))
所以我们求的其实就是 (G(1))。
由后面的式子,(G(1)prod_{k=1}^mp_{T_{i,k}}=sum_{j=1}^n F_j(1)sum_{l|T_i[1:l]=T_j[m-l+1:m]}prod_{k=m-l+1}^mp_{T_{j,k}})。
此外还有 (sum_{j=1}^n F_j(1)=1)。
(n+1) 个方程 (n+1) 个未知数,可以解了。最后代入即得 (G(1))。
接下来是 (S) 初始不为空的情况,也就是我们给它钦定了一些前缀。
首先如果钦定的前缀里就包含了某个 (T_i),那就不用做了。
否则考虑照样列方程。第一个方程是不变的。
第二个方程如何改变呢?关键在于,刚刚列方程时在串尾接上 (T_i) 的操作是在哪里都可以做的,如果使用原来的方程就变成了只有开始随机之后才能接上。实际上还没开始随机的时候也可以往后面加 (T_i),而这样对原函数方程的贡献形如 (sum_{k}q_kx^k)(除 (x) 以外都是常数),而代入 (1) 以后就彻彻底底变成常数了。
因此每次只有常数项有改变,不用每次都高斯消元,只要矩阵求逆后乘向量即可。
时间复杂度 (O(n^3+n^2m+nmk+|R|))。