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\) 步的期望,然后受伪做法的启发,如果匹配成功了就要从根节点从新开始匹配,也就是有转移:

\[dp_{i_j}=\frac{1}{alphabet} \left ( \sum_{flag_v=1} \left(dp_{1,j-1}+1\right)+\sum_{flag_v=0} dp_{v,j-1}\right ) \]

其中 \(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;
}
posted @ 2025-03-17 23:26  LEWISAK  阅读(51)  评论(0)    收藏  举报