标签:
求所有长度为 len 的串s中,最多包含多少个不重叠的子串, 的期望。
开始觉得把s分成若干段这个操作让人无法下手, 但是其实一个简单的贪心就可以了, 因为如果所有子串不互相包涵的话, 一定是能取就取, 如果没有特殊限制的话, 可以把所有包含另一个子串的子串直接删掉或者是在建ac自动机的时候把end传下去(这样记录以当前点为后缀的串中是否含有一个子串)就可以了。
期望 = 这个状态出现的概率 * 贡献(恒为1)
所以就转化成了一个概率dp的问题。
此类题经典的转移状态: f[当前长度][当前在哪里], 然后枚举下一个字符是谁。
10^9 的长度第一维显然是存不下的, 所以要用矩乘来优化
算出f[i][j] 表示从i点走一步到j点的概率, 如果下一个走到的点 end == 1的话就跳回root 就好了。
但是要统计这样跳了多少次, 怎么办呢?
这里有一个小技巧就是单开了一个点T记录这个次数, 每次把下一个点转移到root的时候也转移到这个点上一份, 这样每走完一步这个点的值就是有多少个点在这里跳回了root, 而如果要统计所有步的话就要把每一步的这个值累加起来, 其实只要把 f[T][T] 赋成1就行了。
另外一个问题是这道题 double 是过不去的, 要开long double , 可是我始终get不到long double 应该怎么输出,, %f, %lf, %Lf, %llf 全都不行啊,,,最后强制转化成double 输出了 T T
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> #include <queue> #define MAXN 105 using namespace std; int n, len, lala, root, cnt, next[MAXN][27]; int end[MAXN], fail[MAXN]; struct Matrix{ long double a[MAXN][MAXN]; Matrix(){ for(int i = 0; i <= cnt + 1; i ++) for(int j = 0; j <= cnt + 1; j ++) a[i][j] = 0.0; } }f, ans; Matrix cheng(Matrix a, Matrix b){ Matrix tmp; for(int i = 1; i <= cnt + 1; i ++) for(int k = 1; k <= cnt + 1; k ++) for(int j = 1; j <= cnt + 1; j ++) (tmp.a[i][j] += a.a[i][k] * b.a[k][j]); return tmp; } char s[22]; int newnode(){ cnt ++; memset(next[cnt], -1, sizeof(next[cnt])); return cnt; } void insert(){ int len = strlen(s + 1), now = root; for(int i = 1; i <= len; i ++){ int c = s[i] - ‘a‘; if(next[now][c] == -1) next[now][c] = newnode(); now = next[now][c]; } end[now] = 1; } queue <int> q; void build(){ fail[root] = root; int p = root; for(int i = 0; i < lala; i ++) if(next[p][i] == -1) next[p][i] = root; else fail[next[p][i]] = root, q.push(next[p][i]); while(!q.empty()){ p = q.front(); q.pop(); for(int i = 0; i < lala; i ++) if(next[p][i] == -1) next[p][i] = next[fail[p]][i]; else fail[next[p][i]] = next[fail[p]][i], q.push(next[p][i]), end[next[p][i]] |= end[next[fail[p]][i]]; } } int main(){ scanf("%d%d%d", &n, &len, &lala); root = newnode(); for(int i = 1; i <= n; i ++){ scanf("%s", s + 1); insert(); } build(); int T = cnt + 1; long double ha = (long double)1.0 / lala; for(int i = 1; i <= cnt; i ++){ for(int c = 0; c < lala; c ++) if(end[next[i][c]]){ f.a[i][1] += ha; f.a[i][T] += ha; } else f.a[i][next[i][c]] += ha; } f.a[T][T] = 1; for(int i = 1; i <= cnt + 1; i ++) ans.a[i][i] = 1.0; while(len){ if(len & 1)ans = cheng(ans, f); f = cheng(f, f); len >>= 1; } printf("%.8f\n", (double)ans.a[1][T]); return 0; }
标签:
原文地址:http://www.cnblogs.com/lixintong911/p/4422289.html