Description
Solution
首先我们假设知道每个单词出现的次数,那么对于相同的出现次数只保留一个。
例如出现次数为: a1,a2,a2,a2,a3 ,只保留 a1,a2,a3 。
将它们排序后,我们的到一个从大到小的序列,那么这个序列第
i
次阅读后面
设
Fi,j
为第
i
次阅读,
如果 i−1 次阅读时 j+1 遗忘了,那么有 (Fi−1,j−Fi−1,j+1)⋅p 的概率会记住(表示 i−1 次j能记住且 j+1 记不住的概率)。
总的转移式为: Fi,j=Fi−1,j+1+(Fi−1,j−Fi−1,j+1)⋅p 。
求每个单词出现的次数,kmp卡卡常可能会过(没试过)。当然AC自动机是首选。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
#define N 210
#define K 1010
#define M 200010
using namespace std;
double f[K][N];
char s[N][30];
char ss[K*K];
int wz[N];
int nn=0;
int tr[M][27],dep[M],tot=1,fail[M],up[M],b[M];
struct node{
int x,wz;
}a[N];
bool cmp(node x,node y){
return x.x>y.x;
}
void insert(int x)
{
int l=strlen(s[x]+1),rt=1;
fo(i,1,l)
{
int ch=s[x][i]-'a'+1;
if(!tr[rt][ch]) tr[rt][ch]=++tot;
dep[tr[rt][ch]]=dep[rt]+1,rt=tr[rt][ch];
}
tr[rt][0]=1;
b[rt]=x;
}
void makefail()
{
queue<int> q;
q.push(1);
while(!q.empty())
{
int x=q.front(),p=0;
q.pop();
fo(i,1,26)
{
int v=tr[x][i];
if(!v) continue;
int j=fail[x];
while(j && !tr[j][i]) j=fail[j];
fail[v]=tr[j][i];
q.push(v);
if(!fail[v]) fail[v]=1;
up[v]=b[fail[v]]?fail[v]:up[fail[v]];
}
}
}
void query()
{
int l=strlen(ss+1);
int rt=1;
fo(i,1,l)
{
int j=ss[i]-'a'+1;
while(rt!=1 && !tr[rt][j]) rt=fail[rt];
rt=tr[rt][j];
if(!rt) rt=1;
int p=rt;
while(p>1) a[b[p]].x++,p=up[p];
}
}
int main()
{
int n,q;
scanf("%d",&n);
fo(i,1,n)
{
scanf("%s",s[i]+1);
insert(i),a[i].wz=i;
}
makefail();
scanf("%s",ss+1);
query();
sort(a+1,a+n+1,cmp);
a[0].x=-1;
fo(i,1,n)
{
if(a[i].x!=a[i-1].x) nn++;
wz[a[i].wz]=nn;
}
double p;
scanf("%lf %d",&p,&q);
fo(i,1,nn-1) f[1][i]=1;
f[1][nn]=p;
fo(i,2,q)
{
f[i][nn]=f[i-1][nn]*p;
fo(j,1,nn-1)
f[i][j]=f[i-1][j+1]+(f[i-1][j]-f[i-1][j+1])*p;
}
fo(i,1,n) printf("%.3lf ",f[q][wz[i]]);
}