版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/81747326
题意:给出n(n<=10000)个字符串S[1~n],每个S[i]有权值val[i],随机等概率造一个由小写字母构成的字符串T,Sum = 所有含有子串T的S[i]的val[i]之积,求Sum的期望值。
广义后缀自动机模板题。
广义后缀自动机
这个题比较特殊的地方在于,它不能打了标记后整个自动机同时沿fail链上传标记,必须边打标记边上传,因为出现多次答案只算一次。那么就有人提出了疑问,对于每个串都要单独上传标记,最坏可能每次都遍历整个自动机,不会T吗。
并不会T,因为长度为k的字符串按后缀链接遍历到的点不超过k^2个,又最多只会把整个自动机遍历一遍,所以串S的时间耗费为min(k^2,n),所以平均每个字符的时间耗费为min(k^2,n) / k <= sqrt(n)
所以总的复杂度为O(n * sqrt(n))…….
cin(+ios::sync_with_stdio(false))真好用。
AC Code:
#include<bits/stdc++.h>
#define maxn 600005
#define mod 1000000007
#define Maxc 26
using namespace std;
string s[10005];
int n , ans[maxn];
inline int cut(int a){ return a >= mod ? a - mod : a; }
inline int ksm(int base,int k)
{
int ret=1;
for(;k;k>>=1,base=1ll*base*base%mod) if(k&1) ret=1ll*ret*base%mod;
return ret;
}
int Pow[maxn];
namespace SAM
{
int ch[maxn][Maxc]={} , h[maxn]={} , fail[maxn]={} , Len[maxn]={} , cnt , last , cur;
int vis[maxn]={};
void Setup()
{
fail[0] = -1;
cnt = last = cur = 0;
}
void Insert(int val)
{
Len[cur = ++cnt] = Len[last] + 1 , h[cnt] = 1;
int p = last;
for(;p!=-1 && !ch[p][val];p=fail[p]) ch[p][val] = cur;
if(p==-1) fail[cur] = 0;
else
{
int q = ch[p][val];
if(Len[q] == Len[p]+1) fail[cur] = q;
else
{
int rec = ++cnt;
memcpy(ch[rec] , ch[q] , sizeof ch[q]);
fail[rec] = fail[q] , Len[rec] = Len[p]+1;
for(;p!=-1 && ch[p][val] == q;p=fail[p]) ch[p][val] = rec;
h[fail[cur] = fail[q] = rec] = 1;
}
}
last = cur;
}
void Insert(string s)
{
int len = s.size();
last = 0;
for(int i=0;i<len;i++)
Insert(s[i]-'a');
}
void Update(string s,int val,int tag)
{
int len = s.size() , r = 0;
for(int i=0;i<len;i++)
{
r = ch[r][s[i]-'a'];
for(int tmp = r;tmp!=-1 && vis[tmp] < tag;tmp = fail[tmp])
{
h[tmp] = 1ll * h[tmp] * val % mod;
vis[tmp] = tag;
}
}
}
void dfs(int now)
{
vis[now] = maxn;
if(now)
{
ans[Len[fail[now]]+1] = cut(ans[Len[fail[now]]+1] + h[now]);
ans[Len[now]+1] = cut(ans[Len[now]+1] - h[now] + mod);
}
for(int i=0;i<Maxc;i++)
if(maxn > vis[ch[now][i]])
dfs(ch[now][i]);
}
void Solve()
{
dfs(0);
for(int i=1;i<maxn;i++) ans[i] = cut(ans[i] + ans[i-1]);
for(int i=1;i<maxn;i++) ans[i] = cut(ans[i] + ans[i-1]);
}
};
using namespace SAM;
int main()
{
ios::sync_with_stdio(false);
cin>>n;
Pow[1] = 26;
for(int i=2;i<maxn;i++) Pow[i] = 1ll * Pow[i-1] * Pow[1] % mod;
for(int i=2;i<maxn;i++) Pow[i] = cut(Pow[i] + Pow[i-1]);
fail[0] = -1;
for(int i=1;i<=n;i++)
{
cin>>s[i];
Insert(s[i]);
}
for(int i=1,val;i<=n;i++)
{
cin>>val;
Update(s[i],val,i);
}
Solve();
int q,tmp;
cin>>q;
for(;q--;)
{
cin>>tmp;
cout<<1ll * ans[tmp] * ksm(Pow[tmp],mod-2) % mod<<endl;
}
}