Codeforces Gym103119B. Boring Problem

给定 \(n\) 个长度为 \(m\) 的字符串 \(T_1,T_2,\ldots,T_n\),这些字符串都只包含前 \(k\) 个小写字母。
对于询问串 \(S\),考虑以下过程:
\(\color{orange}{\text{Step}_1}:\) 如果 \(S\) 包含某个 \(T_j\) 作为子串,则结束过程。
\(\color{orange}{\text{Step}_2}:\) 否则,在 \(S\) 之后以 \(p_i\) 的概率添加第 \(i\) 个小写字母,然后回到第 \(1\) 步。
定义 \(f(S;T,p)\) 为过程结束时 \(S\) 的期望长度。
给定一个字符串 \(R\),对于每个 \(i=1,2\ldots,|R|\),求出 \(f(R[1\ldots i];T,p)\)\(10^9+7\) 取模后的结果。
\(1\le n\le 100,1\le nm,|R|\le 10^4,1\le k\le 26\)


添加字母的过程相当于在 \(\text{AC}\) 自动机上移动,终止条件即为碰到叶子节点。

\(E_{u}\) 为当前在 \(u\) 节点,到过程终止还需的期望步数。则有转移

\[E_u=\begin{aligned}\left\{\begin{matrix}1+\sum p_{i}\cdot E_{tr_{u,i}}\quad &u\neq leaf\\0\quad &u=leaf \end{matrix}\right.\end{aligned} \]

显然转移会成环,因此高斯消元,\(\text{AC}\) 自动机上一共有 \(O(nm)\) 个点,消元时间复杂度 \(O(n^3m^3)\),无法通过。

考虑将未知数数量减少。先不考虑连向 \(\text{Fail}\) 指针的边,那么对于一个节点的某个子节点,可以用其它子节点来表示。

具体地,根据 \(E_{u}=1+\sum p_{i}\cdot E_{tr_{u,i}}\) 可以反推 \(E_{tr_{u,x}}=\frac{E_u-1-\sum [i\neq x]p_{i}\cdot E_{tr_{u,i}}}{p_{x}}\)

此时在每个度数为 \(\text{deg}\) 的分叉处只新设了 \(\text{deg}-1\) 个未知元,加上根处的未知元,整个 \(\text{AC}\) 自动机一共设了 \(1+\sum (\text{deg}_i-1)=1+\sum_{i\in\text{AC}} [i\neq root]-\sum_{i\in\text{AC}} [i\neq leaf]=\sum_{i\in\text{AC}}[i=leaf]\le n\) 个未知元。因为可能有重复的字符串所以叶子节点 \(\le n\)

于是神奇地发现此时未知元只有 \(O(n)\) 个,那么直接高斯消元即可,时间复杂度 \(O(n^3+nmk)\)

\(\color{blue}{\text{code}}\)

#include<bits/stdc++.h>
#define eb emplace_back
using namespace std;
typedef long long ll;
const int N=10005,mod=1e9+7;
struct Node{int son[26],flag,fail;}tr[N];
int n,m,k,gn,IV,cnt=1,tot=1,pc[26],E[N][105],ans[N],Mat[105][105];char s[N];
queue<int>q;
inline int qpow(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=(ll)a*a%mod)if(b&1)ans=(ll)ans*a%mod;
	return ans;
}
inline void insert(char *s){
	int rt=1;
	for(int i=0;i<m;++i){
		int ch=s[i]-'a';
		if(!tr[rt].son[ch])tr[rt].son[ch]=++cnt;
		rt=tr[rt].son[ch];
	}
	tr[rt].flag=1;
}
inline void getFail(){
	for(int i=0;i<26;++i)tr[0].son[i]=1;
	q.push(1);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			int v=tr[u].son[i],Fail=tr[u].fail;
			if(!v){tr[u].son[i]=tr[Fail].son[i];continue;}
			tr[v].fail=tr[Fail].son[i],q.push(v);
		}
	}
}
inline void calcMatrix(){
	E[1][0]=0,E[1][1]=1,q.push(1);
	while(!q.empty()){
		int u=q.front();q.pop();
		if(tr[u].flag){++gn;for(int i=0;i<=tot;++i)Mat[gn][i]=E[u][i];continue;}
		vector<int>son;
		int p0=-1,id=0;
		for(int i=0;i<26;++i){
			int v=tr[u].son[i];
			if(v^tr[tr[u].fail].son[i]){
				q.push(v),son.eb(v);
				if(!~p0)p0=pc[i],id=v;
			}
		}
		for(int i=1;i<son.size();++i)E[son[i]][0]=0,E[son[i]][++tot]=1;
		int pv=qpow(p0,mod-2);
		E[id][0]=(ll)(E[u][0]+mod-1)*pv%mod;
		for(int i=1;i<=tot;++i)E[id][i]=(ll)E[u][i]*pv%mod;
		for(int i=0;i<26;++i){
			int v=tr[u].son[i],coef=(ll)pc[i]*pv%mod;
			if(v^id)for(int j=0;j<=tot;++j)(E[id][j]+=mod-(ll)coef*E[v][j]%mod)%=mod;
		}
	}
	for(int i=1;i<=tot;++i)Mat[i][tot+1]=(mod-Mat[i][0])%mod;
}
inline void guass(int(*a)[105],int n){
	for(int i=1,r;i<=n;++i){
		for(int j=i;j<=n;++j)if(a[j][i]){r=j;break;}
		if(r^i)swap(a[i],a[r]);
		int v=qpow(a[i][i],mod-2);
		for(int j=i;j<=n+1;++j)a[i][j]=(ll)a[i][j]*v%mod;
		for(int k=1;k<=n;++k)if(k!=i&&a[k][i])
			for(int j=n+1;j>=i;--j)
				(a[k][j]+=mod-(ll)a[i][j]*a[k][i]%mod)%=mod;
	}
}
inline void calc(){
	for(int i=1;i<=cnt;++i){
		ans[i]=E[i][0];
		for(int j=1;j<=tot;++j)
			(ans[i]+=(ll)Mat[j][tot+1]*E[i][j]%mod)%=mod;
	}
	ans[0]=ans[1];
}
inline void query(char *s){
	int rt=1,fl=0,len=strlen(s);
	for(int i=0;i<len;++i){
		rt=tr[rt].son[s[i]-'a'];
		if(tr[rt].flag)fl=1;
		printf("%d\n",(i+1+(1-fl)*ans[rt])%mod);
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&k),IV=qpow(100,mod-2);
	for(int i=0;i<k;++i)scanf("%d",pc+i),pc[i]=(ll)pc[i]*IV%mod;
	for(int i=1;i<=n;++i)scanf("%s",s),insert(s);scanf("%s",s);
	getFail();
	calcMatrix();
	guass(Mat,tot);
	calc();
	query(s);
	return 0;
}
posted @ 2022-06-07 20:21  Samsara-soul  阅读(47)  评论(0编辑  收藏  举报