作文 题解
牛牛写作文
https://ac.nowcoder.com/acm/contest/11183/F
作文 题解
令为题中的重要串,为的长度,显然我们需要维护写出的字符串与的匹配。
考虑用 来求解该题。令 表示已经匹配到 的第 位时,在结束前匹配整个串的概率。由于 在第一次出现以后,就无需考虑后继的情况,那么边界条件就是 ,答案就是 。
考虑状态接上句子的转移,设这个后继状态为:
-
如果在接上句子的中途就出现了串整串,则;
-
否则,令 就是接上 这个句子后,与 串匹配的长度。
则对于每个,可以从其所有后继状态中得到转移:
其中为这一轮结束的概率。
预处理
可以考虑用(自动机)维护,枚举串,依次接上每一位。
令表示出现的句子的总长,则暴力的复杂度为 (因为在新接上串时,还要考虑前个字符的失配);
而用自动机维护的复杂度为。
求解
对以上提到的转移,容易发现每个的后继构成的转移关系不具有拓扑序,即实际的转移可能是这种循环形式:
此时可以看成是以为元的 元线性方程组,可以使用高斯消元进行求解,复杂度为。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int,int> Pii;
#define reg register
#define mp make_pair
#define pb push_back
#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
char IO;
template <class T=int> T rd(){
T s=0; int f=0;
while(!isdigit(IO=getchar())) f|=IO=='-';
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
bool Mbe;
const int N=210,INF=1e9+10,P=1e9+7;
int n,m;
char s[N];
char t[1010][N];
int G[N][N],nxt[N][26],fail[N];
ll qpow(ll x,ll k=P-2) {
ll res=1;
for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
return res;
}
bool Med;
int main(){
m=rd();
rep(i,1,m) scanf("%s",t[i]+1);
scanf("%s",s+1),n=strlen(s+1);
rep(i,1,n) nxt[i-1][s[i]-'a']=i;
rep(i,1,n) {
rep(j,0,25) if(nxt[i][j]) fail[nxt[i][j]]=nxt[fail[i]][j];
else nxt[i][j]=nxt[fail[i]][j];
}
rep(i,0,n-1) {
rep(j,1,m) {
int p=i;
for(int k=1;t[j][k] && p!=n;++k) p=nxt[p][t[j][k]-'a'];
G[i][p]++;
}
}
rep(i,0,n-1) G[i][i]-=m+1,Mod2(G[i][i]);
G[n][n]=G[n][n+1]=1;
rep(i,0,n) {
if(!G[i][i]){
rep(j,i+1,n) if(G[j][i]) {
swap(G[i],G[j]);
break;
}
}
assert(G[i][i]);
ll inv=qpow(G[i][i]);
rep(j,i,n+1) G[i][j]=G[i][j]*inv%P;
rep(j,0,n) if(i!=j) drep(k,n+1,i) G[j][k]=(G[j][k]+1ll*(P-G[j][i])*G[i][k])%P;
}
printf("%d\n",G[0][n+1]);
}