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\) 节点,到过程终止还需的期望步数。则有转移
显然转移会成环,因此高斯消元,\(\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)\)。
#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;
}