传送门
我的板蓝根
前言
这个题的数据范围及其出卖解法,其实很简单。
题目大意
定义一个字符串的权值为将其分割后子串与 \(N\) 个文本串相等个数的最大值,求:在由前 \(alphabet\) 个小写字母组成的长度为 \(len\) 的任意字符串中随机选择出的字符串的期望权值。
题解
看到这个题第一反应是一个常用小技巧:为了减小误差,把期望拆成权值和除以总方案数,然后看一眼数据范围:\(len \le 10^9\),直接就死了。
但是这也启示了我们:能通过 \(10^9\) 级别的算法,要么复杂度根本不带 \(len\) 要么是 \(\log\) 级别的,考虑后者发现只有矩阵快速幂看起来比较可行。
然后再看到 \(N \le 5\) 和模式串的长度不超过 \(15\),那这应该就是矩阵的宽了。
注意到样例说 \(\text {aabb}\) 不能拆成 \(\text {aa}\) 和 \(\text {abb}\),也就是说一个字节不能给多次贡献,于是有一个很有意思的伪做法就是将文本串“堆”起来,然后转移是要么在 \(i-len\) 的位置“堆”一个文本串,要么放杂的字符,比如下图是一种权值为4的情况:
但如果杂字符联合成了一个文本串就废了,而且拆成 \(\text {ab}\) 不如拆成 \(\text {a}\) 和 \(\text {b}\),如过有这种情况也会废,但这个伪做法启示我们文本串匹配成功后就要从头开始匹配。
于是我们考虑一个文本匹配多个模式看着就像是 AC 自动机,进一步考虑 AC 自动机上 dp。
设 \(dp_{i,j}\) 表示从 \(i\) 出发走 \(j\) 步的期望,然后受伪做法的启发,如果匹配成功了就要从根节点从新开始匹配,也就是有转移:
其中 \(v\) 是 \(i\) 在 AC 自动机上的子节点,\(flag_i\) 表示到 \(i\) 有没有后缀是文本串,然后矩阵快速幂优化即可。
代码
#include<bits/stdc++.h>
#define LF long double
using namespace std;
namespace FFF{
const int mod=1e4+7;
struct AAA{int son[30],flag,fail;
}tr[100100];
struct QQQ{LF mapp[110][110];int x,y;
}a;
int n,m,cnt=1,ans,alp,len;
char s[100100];
queue<int> q;
void insert(char s[]){int u=1,len=strlen(s);for(int j=0;j<len;j++){int v=s[j]-'a';if(!tr[u].son[v]){tr[u].son[v]=++cnt;}u=tr[u].son[v];}tr[u].flag=1;
}
void init(){for(int i=0;i<26;i++){tr[0].son[i]=1;}q.push(1);while(!q.empty()){int u=q.front(),f=tr[u].fail;q.pop();for(int i=0;i<26;i++){int v=tr[u].son[i];if(!v){tr[u].son[i]=tr[f].son[i];continue;}tr[v].fail=tr[f].son[i];tr[v].flag|=tr[tr[v].fail].flag;q.push(v);}}
}
QQQ cheng(QQQ x,QQQ y){QQQ ans;for(int i=1;i<=x.x;i++){for(int j=1;j<=x.x;j++){ans.mapp[i][j]=0.0;}} for(int i=1;i<=x.x;i++){for(int j=1;j<=x.x;j++){for(int k=1;k<=x.y;k++){ans.mapp[i][j]+=(LF)x.mapp[i][k]*y.mapp[k][j];}}}ans.x=ans.y=x.x;return ans;
}
QQQ qpow(QQQ x,int y){QQQ aaa=x;y--;while(y){if(y&1){aaa=cheng(aaa,x);}x=cheng(x,x);y>>=1;}return aaa;
}
string main(){cin>>n>>len>>alp;for(int i=1;i<=n;i++){cin>>s;insert(s);}init();a.x=a.y=cnt+1;a.mapp[cnt+1][cnt+1]=1;for(int i=1;i<=cnt;i++){for(int j=0;j<alp;j++){if(tr[tr[i].son[j]].flag){a.mapp[i][1]+=1.0/(LF)alp;a.mapp[i][cnt+1]+=1.0/(LF)alp;}else{a.mapp[i][tr[i].son[j]]+=1.0/(LF)alp;}}}a=qpow(a,len);
// cout<<a.mapp[1][cnt+1];printf("%Lf",a.mapp[1][cnt+1]);return "woshiyuanshenwanjia!!!";
}
}
signed main(){FFF::main();return 0;
}