【题解】Luogu P4569 [BJWC2011] 禁忌
考虑如果暴力 DP,设 \(f_{i,j}\) 为当前的串长为 \(i\),在 AC 自动机的 \(j\) 节点的概率。转移时枚举在后面加的字符 \(k\),如果加上 \(k\) 后匹配上了一个禁忌串就直接回到根节点,同时给答案贡献,否则就继续匹配。\(len\) 在 \(10^9\),时间复杂度会超标,于是矩阵加速。
如果 \(j\) 节点可以贡献给 \(k\) 节点,那就将转移矩阵第 \(j\) 行第 \(k\) 列加上一个 \(\frac{1}{alphabet}\)。多开一行存答案,在需要累计答案时将贡献加给答案。
时间复杂度 \(O((\sum|T_i|)^3\log len)\)。
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
int n,m,tr[80][30];
int tot,fail[80];
double ab;
bool end[80];
string s;
queue<int> q;
struct juz{
double mat[80][80];
juz(){
for(int i=0;i<=tot;i++){
for(int j=0;j<=tot;j++){
mat[i][j]=0;
}
}
}
il double*operator[](int x){
return mat[x];
}
il juz operator*(juz x)const{
juz res;
for(int i=0;i<=tot;i++){
for(int j=0;j<=tot;j++){
for(int k=0;k<=tot;k++){
res[i][j]+=mat[i][k]*x[k][j];
}
}
}
return res;
}
}bas;
il juz qpow(juz x,int y){
juz res;
for(int i=0;i<=tot;i++){
res[i][i]=1;
}
while(y){
if(y&1){
res=res*x;
}
y>>=1,x=x*x;
}
return res;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m>>ab;
for(int i=1,p;i<=n;i++){
cin>>s;
p=0;
for(int j=0,d;j<s.size();j++){
d=s[j]-'a';
if(!tr[p][d]){
tr[p][d]=++tot;
}
p=tr[p][d];
}
end[p]=1;
}
for(int i=0;i<ab;i++){
if(tr[0][i]){
q.push(tr[0][i]);
}
}
while(q.size()){
int u=q.front();
q.pop();
for(int i=0;i<ab;i++){
if(tr[u][i]){
fail[tr[u][i]]=tr[fail[u]][i];
end[tr[u][i]]|=end[fail[tr[u][i]]];
q.push(tr[u][i]);
}
else{
tr[u][i]=tr[fail[u]][i];
}
}
}
// for(int i=0;i<=tot;i++){
// cout<<fail[i]<<" ";
// }
// puts("");
tot++;
for(int i=0;i<tot;i++){
for(int j=0;j<ab;j++){
if(end[tr[i][j]]){
bas[i][0]+=1.0/ab;
bas[i][tot]+=1.0/ab;
}
else{
bas[i][tr[i][j]]+=1.0/ab;
}
}
}
bas[tot][tot]=1;
printf("%.10f",qpow(bas,m)[0][tot]);
return 0;
}
}
int main(){return asbt::main();}

浙公网安备 33010602011771号