• bzoj2553 禁忌


    题目链接

    题意

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

    思路

    首先想如果这个长度为(len)的字符串已经给出了。应该怎么算贡献。
    只要贪心的在(AC)自动机上走,如果走到了字典中字符串的结尾,就回到根节点,然后重新走。
    现在没有给出这个字符串,在(AC)自动机上表现为每个点的每个儿子都有(frac{1}{alphabet})的概率存在。
    这个题要求求期望,根据期望的线性性。只要求出每个字符串被经过的概率之和( imes)贡献,然后加起来就行了。因为每个字符串的贡献都是(1),所以只要将他们的概率之和求出来就行了。
    然后考虑如何求出来每个字符串被经过的概率。
    根据上面的贪心思路。我们可以用(f[k][i])表示走了(k)步,走到(j)的概率。只要用(f[k-1][fa[i]] imes frac{1}{alphabet})就行了。
    因为(len)到了(10 ^ 9),所以考虑矩阵快速幂优化,我们可以把从每个点走到另一个点的概率用一个(n imes 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;
    }
    	
    
  • 相关阅读:
    ES6实现小案例--自定义弹框
    ES6 字符串、数值与布尔值、函数参数的解构赋值
    ES6 对象的解构赋值
    ES6 数组的解构赋值
    CentOS7安装mysql后无法启动服务,提示Unit not found
    CentOS7安装MySQL报错,解决Failed to start mysqld.service: Unit not found
    redis修改密码
    redis入门
    如何在本地远程连接linux虚拟机上面的mysql
    Linux下彻底卸载mysql详解
  • 原文地址:https://www.cnblogs.com/wxyww/p/10340970.html
Copyright © 2020-2023  润新知