P4569 [BJWC2011] 禁忌♂题解
前言
这个题的数据范围及其出卖解法,其实很简单。
题目大意
定义一个字符串的权值为将其分割后子串与 \(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;
}

浙公网安备 33010602011771号