BJOI2011 day3 禁忌 taboo

2 篇文章 0 订阅
1 篇文章 0 订阅

描述

随机吐出一个长为lenth的字符串,求E("最多包含多少个禁忌词")。E表示数学期望。

思路

AC自动机+DP+矩阵乘法。。。

代码

注意:prG函数用来debug用的,请无视。
注意看输入格式。
注意AC自动机的计算时的关系。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define NMAX 101
int alpha,num;
struct node
{
	int name;
	bool end;
	node *next[26],*fa,*fail;
	node(){end=0;fail=NULL;memset(next,0,sizeof(next));}
}epool[NMAX],*etop,*head;
void init()
{
	etop=epool;
	head=etop++;
}
void add(char *c)
{
	node *p=head;
	for (int i=0;i<strlen(c);i++)
	{
		int x=c[i]-'a';
		if (!p->next[x])
		{
			etop->fa=p;
			etop->name=x;
			p->next[x]=etop++;
		}
		p=p->next[x];
	}
	p->end=1;
}
node *Q[NMAX];
void build_ac()
{
	int top,bot;
	top=bot=0;
	Q[bot++]=head;
	node *t,*p;
	while(top<bot)
	{
		t=Q[top++];
		if (t->fa==head) t->fail=head;
		else if (t!=head)
		{
			int x=t->name;
			p=t->fa->fail;
			t->fail=p->next[x];
			t->end|=p->next[x]->end;
		}
		for (int i=0;i<alpha;i++)
		{
			if (t->next[i]) Q[bot++]=t->next[i];
			else
			{
				if (t==head) t->next[i]=head;
				else t->next[i]=t->fail->next[i];
			}
		}
	}
}
struct matrix
{
	long double a[NMAX][NMAX];
	matrix(){memset(a,0,sizeof(a));}
};
matrix operator * (const matrix &x,const matrix &y)
{
	matrix ret;
	for (int i=0;i<num;i++)
		for (int j=0;j<num;j++)
			for (int k=0;k<num;k++)
				ret.a[i][j]+=x.a[i][k]*y.a[k][j];
	return ret;
}
double pow(matrix g,int n)
{
	matrix ret;
	for (int i=0;i<num;i++) ret.a[i][i]=1;
	while (n)
	{
		if (n&1) ret=ret*g;
		g=g*g;
		n>>=1;
	}
	return ret.a[0][num-1];
}
matrix G;
void buildG()
{
	node *t;
	num=etop-epool+1;
	for (int i=0;i<num-1;i++)
	{
		for (int j=0;j<alpha;j++)
		{
			t=epool+i;
			int jaddr=t->next[j]-epool;
			if (t->next[j]->end)
			{
				G.a[i][0]+=1./alpha;G.a[i][num-1]+=1./alpha;
			}
			else
				G.a[i][jaddr]+=1./alpha;
		}
	}
	G.a[num-1][num-1]=1;
}
void prG()
{
	printf("NUM=%d\n",num);
	for (int i=0;i<num;i++)
	{
		for (int j=0;j<num;j++)
			printf("%.3f ",G.a[i][j]);
		printf("\n");
	}
}
int main()
{
	init();
	char tmp[20];
	int n,lenth;
	scanf("%d%d%d",&n,&lenth,&alpha);
	for (int i=0;i<n;i++)
		scanf("%s",tmp),add(tmp);
	build_ac();
	
	buildG();
	//prG();
	/*for (int i=0;i<num-1;i++)
	{
		printf("%d :",i);
		for (int j=0;j<alpha;j++)
		{
			if (epool[i].next[j])
				printf("%d",epool[i].next[j]-epool);
		}
		printf("\n");
	}*/
	printf("%f\n",pow(G,lenth));
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值