bzoj2553: [BeiJing2011]禁忌
传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2553
思路:第一件事当然是建立AC自动机。。。
现在我们建好了AC自动机,那么我们就在AC自动机上走,走到一个终止节点就算我们找到一个禁忌串,然后返回根节点重新匹配。
和bzoj1030类似,考虑DP,设f[i][j]为现在长度为i,走到j号节点的期望。
转移就是枚举下一个字符。下一个字符是终止节点就跳回root下次重新走,ans的期望就可以增加1/字符集大小。
因为长度最大有10^9,显然直接DP会无论空间还是时间都会爆炸。。。
所以用矩阵乘法+快速幂加速转移
现在考虑怎么处理出初始的转移矩阵
先算出a[i][j]表示i一步到j的概率
用bfs就可以实现,如果j是i的儿子,那么a[i][j]+=1/字符集大小
为了方便我们新建一个节点n=cnt(总结点数)+1
每次转移root时也转移到它
那么a[i][n]就是i走一步匹配到禁忌串的概率。
要把所有步都累加出来,把a[n][n]赋为1就可以了
因为这样下一次计算时b[root][n]=....+b[root][n]*a[n][n]+....
就可以把上次的答案都累加起来了。
自乘x次后,因为贡献永远是1,所以a[root][n]就表示root走x步遇到禁忌串的期望,也就是答案。
最后吐槽一句:卡精度简直丧心病狂...不开long double就不让过...
#include<cstdio> #include<cstring> #include<algorithm> const int maxn=110; using namespace std; struct matrix{ long double a[maxn][maxn]; void clear(){for (int i=0;i<maxn;i++) for (int j=0;j<maxn;j++) a[i][j]=0.0;} }ans,f; int n,K,dsiz,num;char s[maxn];bool bo[maxn]; inline matrix operator*(matrix a,matrix b){ matrix res;res.clear(); for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) for (int k=0;k<=n;k++) res.a[i][j]+=a.a[i][k]*b.a[k][j]; return res; } void qpow(){for (;K;K>>=1,f=f*f) if (K&1) ans=ans*f;} struct AC_DFA{ int tot,ch[maxn][26],fail[maxn],q[maxn],head,tail;bool end[maxn]; void insert(){ int len=strlen(s),p=0; for (int i=0;i<len;p=ch[p][s[i]-'a'],i++) if (!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++tot; end[p]=1; } void getfail(){ head=0,q[tail=1]=0,fail[0]=-1; while (head!=tail){ int x=q[++head]; for (int i=0;i<dsiz;i++) if (ch[x][i]) q[++tail]=ch[x][i],fail[ch[x][i]]=x==0?0:ch[fail[x]][i]; else ch[x][i]=x==0?0:ch[fail[x]][i]; end[x]|=end[fail[x]]; } } void build(){ head=0,q[tail=1]=0,bo[0]=1; long double tmp=1.0/dsiz; while (head!=tail){ int x=q[++head]; for (int i=0;i<dsiz;i++){ if (!bo[ch[x][i]]) bo[ch[x][i]]=1,q[++tail]=ch[x][i]; if (end[ch[x][i]]) f.a[x][n]+=tmp,f.a[x][0]+=tmp; else f.a[x][ch[x][i]]+=tmp; } } } }T; int main(){ scanf("%d%d%d",&num,&K,&dsiz); for (int i=1;i<=num;i++) scanf("%s",s),T.insert(); n=T.tot+1,T.getfail(),T.build(); for (int i=0;i<=n;i++) ans.a[i][i]=1; f.a[n][n]=1;qpow(); printf("%.7f\n",(double)ans.a[0][n]); return 0; }