BZOJ2553 [BeiJing2011]禁忌 AC自动机 矩阵

原文链接http://www.cnblogs.com/zhouzhendong/p/8196279.html


题目传送门 - BZOJ2553


题意概括

  引用一下lych大佬的:

  在字母只有前alphabet时,给定N个串,求长度为len的串包含这些N个串的个数最大值的期望值。


题解

  我们首先发现总共的字符个数才就75个,那么闭着眼睛先建一个AC自动机,Trie图建好。反正代码不长。

  然后我们发现长度很大,显然就是要往矩阵快速幂那里考虑。

  我们可以用矩阵来快速计算到达AC自动机的每一个位置的概率。

  然后我们考虑原问。

  对于一个串,把它划分成包含最多的禁忌串数的方案怎么做?

  我们可以贪心的来,从头开始沿着字符串在AC自动机上面走,每一次到一个结束点就划分。这样一定是最优的。

  然后回到矩阵中。

  假如说我们考虑DP的话,那么每到一个结束点,都要统计一下当前概率对答案的贡献,就是概率*1

  那么我们可以把它记入答案中。

  同理,我们可以在矩阵中多开一列,用来维护答案。

 


代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long double LD;
const int S=80;
struct Trie{
	int fail,e,Next[26];
	void clear(){
		fail=e=0;
		memset(Next,0,sizeof Next);
	}
}tree[S];
int n,len,alpha,cnt;
void build(char ch[]){
	int rt=1,t,len=strlen(ch);
	for (int i=0;i<len;i++){
		t=ch[i]-'a';
		if (!tree[rt].Next[t]){
			tree[++cnt].clear();
			tree[rt].Next[t]=cnt;
		}
		rt=tree[rt].Next[t];
	}
	tree[rt].e=1;
}
void build_AC(){
	int q[S],head=0,tail=0;
	int rt,son,k;
	q[++tail]=1,tree[0].fail=1;
	while (head<tail){
		int rt=q[++head];
		for (int i=0;i<alpha;i++){
			son=tree[rt].Next[i];
			if (!son){
				tree[rt].Next[i]=tree[tree[rt].fail].Next[i];
				continue;
			}
			k=tree[rt].fail;
			while (!tree[k].Next[i])
				k=tree[k].fail;
			tree[son].fail=tree[k].Next[i];
			tree[son].e|=tree[tree[k].Next[i]].e;
			q[++tail]=son;
		}
	}
}
int m;
struct Mat{
	LD v[S][S];
	Mat (){}
	Mat (int x){set(x);}
	void print(){
		for (int i=1;i<=m;i++,puts(""))
			for (int j=1;j<=m;j++)
				printf("%5.3Lf ",v[i][j]);
		puts("");
	}
	void set(int x){
		memset(v,0,sizeof v);
		if (x==1)
			for (int i=1;i<=m;i++)
				v[i][i]=1;
	}
	Mat operator * (Mat b){
		Mat ans(0);
		for (int i=1;i<=m;i++)
			for (int j=1;j<=m;j++)
				for (int k=1;k<=m;k++)
					ans.v[i][j]+=v[i][k]*b.v[k][j];
		return ans;
	}
	Mat operator ^ (int y){
		Mat ans(1),x=*this;
		while (y){
			if (y&1)
				ans=ans*x;
			x=x*x;
			y>>=1;
		}
		return ans;
	}
}M(0);
char s[S];
int main(){
	scanf("%d%d%d",&n,&len,&alpha);
	cnt=1;
	tree[0].clear();
	tree[1].clear();
	for (int i=0;i<alpha;i++)
		tree[0].Next[i]=1;
	for (int i=1;i<=n;i++){
		scanf("%s",s);
		build(s);
	}
	build_AC();
	m=cnt+1;
	LD addv=1.0/(1.0*alpha);
	for (int i=1;i<=cnt;i++)
		for (int j=0;j<alpha;j++)
			if (tree[tree[i].Next[j]].e)
				M.v[i][1]+=addv,M.v[i][m]+=addv;
			else
				M.v[i][tree[i].Next[j]]+=addv;
	M.v[m][m]=1;
	Mat Mans=M^len;
	printf("%.10lf",(double)Mans.v[1][m]);
	return 0;
}

  

 

posted @ 2018-01-04 22:08  zzd233  阅读(247)  评论(0编辑  收藏  举报