【題解】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號