bzoj2553 禁忌

题目链接

题意

给出一个\(n\)个字符串的字典。对于一个字符串,他的贡献是这个字符串中最多的在字典中出现的不重叠子串的数量。
然后问一个长度为\(len\)的,字符集为前\(alphabet\)个字符的字符串的贡献期望是多少。

思路

首先想如果这个长度为\(len\)的字符串已经给出了。应该怎么算贡献。
只要贪心的在\(AC\)自动机上走,如果走到了字典中字符串的结尾,就回到根节点,然后重新走。
现在没有给出这个字符串,在\(AC\)自动机上表现为每个点的每个儿子都有\(\frac{1}{alphabet}\)的概率存在。
这个题要求求期望,根据期望的线性性。只要求出每个字符串被经过的概率之和\(\times\)贡献,然后加起来就行了。因为每个字符串的贡献都是\(1\),所以只要将他们的概率之和求出来就行了。
然后考虑如何求出来每个字符串被经过的概率。
根据上面的贪心思路。我们可以用\(f[k][i]\)表示走了\(k\)步,走到\(j\)的概率。只要用\(f[k-1][fa[i]]\times \frac{1}{alphabet}\)就行了。
因为\(len\)到了\(10 ^ 9\),所以考虑矩阵快速幂优化,我们可以把从每个点走到另一个点的概率用一个\(n\times n\)矩阵来表示。然后将这个矩阵自乘\(len\)遍就行了。
然后考虑这个矩阵应该怎么得到。首先要在矩阵上新建一个统计答案的点。只要在\(AC\)自动机上\(bfs\)一遍,如果儿子被打过结束标记了,那么就将当前点走到新建点和根节点的概率\(+\frac{1}{alphabet}\).否则就将当前节点走到孩子节点的概率\(+\frac{1}{alphabet}\)
然后矩阵快速幂就行了。

代码

/*
* @Author: wxyww
* @Date:   2019-01-31 08:45:43
* @Last Modified time: 2019-01-31 10:03:47
*/
#include<cstring>
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<ctime>
#include<bitset>
using namespace std;
typedef long long ll;
typedef long double ld;
ll read() {
	ll x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
queue<int>q;
struct Mat {
	ld a[100][100];
	int n;
	Mat() {
		n = 0;
		memset(a,0,sizeof(a));
	}
	Mat(int x) {
		n = x;
		for(int i = 0;i <= n;++i) a[i][i] = 1;
	}
}C;
char s[20];
ld tmp;
int trie[100][27],tot,bz[400];
void ins() {
	int len = strlen(s + 1);
	int now = 0;
	for(int i = 1;i <= len;++i) {
		int x = s[i] - 'a';
		if(!trie[now][x]) trie[now][x] = ++tot;
		now = trie[now][x];
	}
	bz[now] = 1;
}
int fail[400],vis[400];
int alp;
void build() {
	for(int i = 0;i < alp;++i) if(trie[0][i]) q.push(trie[0][i]);
	while(!q.empty()) {
		int now = q.front();q.pop();
		for(int i = 0;i < alp;++i) {
			if(trie[now][i]) fail[trie[now][i]] = trie[fail[now]][i],q.push(trie[now][i]);
			else trie[now][i] = trie[fail[now]][i]; 
		}
	}
}

void bfs() {
	while(!q.empty()) q.pop();
	q.push(0);vis[0] = 1;
	while(!q.empty()) {
		int now = q.front();q.pop();
		for(int i = 0;i < alp;++i) {
			if(!vis[trie[now][i]]) vis[trie[now][i]] = 1,q.push(trie[now][i]);
			if(bz[trie[now][i]]) C.a[now][0] += tmp, C.a[now][tot] += tmp;
			else C.a[now][trie[now][i]] += tmp;
		}
	}
}

Mat operator * (Mat x,Mat y) {
	int n = x.n;
	Mat ret;
	ret.n = n;
	for(int k = 0;k <= n;++k) {
		for(int i = 0;i <= n;++i) {
			for(int j = 0;j <= n;++j) {
				ret.a[i][j] += x.a[i][k] * y.a[k][j];
			}
		}
	}
	return ret;
}
Mat operator ^ (Mat x,int y) {
	int n = x.n;
	Mat ret(n);
	for(;y;y >>= 1,x = x * x) {
		if(y & 1) ret = ret * x;
	}
	return ret;
}
int main() {
	int n = read(),len = read();
	 cin>>alp;
	tmp = (ld)1 / (ld)alp;
	for(int i = 1;i <= n;++i) {
		scanf("%s",s + 1);
		ins();
	}
	build();
	++tot;
	bfs();
	C.n = tot;
	C.a[tot][tot] = 1;
	C = C ^ len;
	printf("%.7Lf",C.a[0][tot]);
	return 0;
}
	
posted @ 2019-01-31 10:50  wxyww  阅读(158)  评论(0编辑  收藏  举报