题目大意:
有n个单词,有一个长度为m的文章,现在你先需要知道每个单词在文章内出现的次数。
现在一共进行k轮,每一轮有p的概率保留出现次数最小的那些单词(不保留就是把那些单词删掉)。
求n轮以后,每个单词保留下来的概率.
1<=n<=200,1<=单词长度<=20,1<=m<=10^6
题解:
分两个部分。
第一部分就直接上ac自动机,可以打up来优化。
ac自动机up优化:
up[x]表示x和x的fail链上的第一个是单词结尾的点。
这样查询的时候就会很快(贴瓷砖也有用的上)。
注意单词可能会有重复,单词结尾需要用前向星维护。
第二部分就是概率dp.
设f[i][j]表示第i轮,出现次数第j大的单词们活下来的概率(出现次数相同的算同一个)
f[i][j] = f[i - 1][j - 1] +f[i - 1][j] * p
Code:
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int Maxn = 205, Maxm = 1000005;
double p;
char a[Maxn][25], s[Maxm];
int n, ls, cc, ans[205], b[205], d[10000], next[10000][27], fail[10000], up[10000], tt;
int final[10000], tot;
struct edge {
int to, next;
}e[205];
double f[1005][205], sum[1005];
void link(int x, int y) {
e[++ tot].next = final[x], e[tot].to = y, final[x] = tot;
}
void insert(int p, char *a) {
int len = strlen(a + 1), k = 1;
fo(i, 1, len) {
if(!next[k][a[i] - 'a']) next[k][a[i] - 'a'] = ++ tt;
k = next[k][a[i] - 'a'];
}
link(k, p);
}
void make_ac() {
int l = 0, r = 0; d[++ r] = 1;
while(l < r) {
l ++; int x = d[l];
fo(i, 0, 25) {
int y = next[x][i];
if(!y) continue;
int j = fail[x];
while(j && !next[j][i]) j = fail[j];
if(next[j][i]) j = next[j][i];
fail[y] = j; if(!fail[y]) fail[y] = 1;
up[y] = y; if(!final[y] && y != 1) up[y] = up[fail[y]];
d[++ r] = y;
}
}
}
void Init() {
tt = 1;
scanf("%d", &n);
fo(i, 1, n) {
scanf("%s", a[i] + 1);
insert(i, a[i]);
}
make_ac();
scanf("%s", s + 1);
ls = strlen(s + 1);
}
void Work() {
int j = 1;
fo(i, 1, ls) {
while(j != 1 && !next[j][s[i] - 'a']) j = fail[j];
if(next[j][s[i] - 'a']) j = next[j][s[i] - 'a'];
int z = up[j];
while(z) {
for(int k = final[up[z]]; k; k = e[k].next)
ans[e[k].to] ++;
z = up[fail[z]];
}
}
}
bool rank_b(int x, int y) {
return ans[x] < ans[y];
}
void End() {
scanf("%lf", &p); scanf("%d", &cc);
fo(i, 1, n) b[i] = i;
sort(b + 1, b + n + 1, rank_b);
fo(j, 2, n) f[1][j] = 1;
f[1][1] = p;
fo(i, 2, cc) {
int k = 0;
fo(j, 1, n) {
if(j == 1 || ans[b[j]] != ans[b[j - 1]]) {
k ++;
f[i][k] = f[i - 1][k - 1] + (f[i - 1][k] - f[i - 1][k - 1]) * p;
}
}
}
int k = 0;
fo(i, 1, n) {
if(i != 1 && ans[b[i]] == ans[b[i - 1]])
sum[b[i]] = sum[b[i - 1]]; else k ++, sum[b[i]] = f[cc][k];
}
fo(i, 1, n) printf("%.3lf ", sum[i]);
}
int main() {
Init();
Work();
End();
}