[关闭]
@xiaoziyao 2021-01-09T11:53:23.000000Z 字数 2123 阅读 813

CF86C Genetic engineering解题报告

解题报告


CF86C Genetic engineering解题报告:

更好的阅读体验

题意

分析

一道较套路的AC自动机dp题,但我没想出来/kk。

考虑先对所有给出的字符串建出AC自动机。

套路地设状态为在AC自动机上匹配到了位置,且已经用了个字符的方案总数。

我们发现在AC自动机上转移边分为trie树上原有的转移边和AC自动机添加的转移边,考虑直接在上面dp,可以正常进行trie树边的转移,对于AC自动机的转移,我们能转移当且仅当当前结点是一个字符串结束位置。

上面的转移方式乍一看好像没错,因为转移AC自动机添加的转移边时需要跳fail边,变成原来匹配串的后缀,因此会让一些没有覆盖的点无法覆盖。

但是我们考虑一种情况:先通过trie树上的匹配到了一个结束位置,然后往下跳一次trie树边,并跳一次AC自动机添加的转移边,最后再跳一次到了另一个位置较深的结束位置。

这样这个结束位置的长度足以覆盖前面没有覆盖的内容,这种方式仍然是合法的。

也就是说,我们跳fail边时,可能去除的前缀已经覆盖过了,因此这样的状态是不能转移的。

考虑另一种状态设计方式:表示在AC自动机上匹配到了位置,用了个字符,且后个字符还没有覆盖的方案数。

我们定义一个辅助数组,表示到了AC自动机上的位置,以为结尾的最长字符串长度为多少。

这样的转移也非常清晰:我们当前的状态为,通过AC自动机的转移边(无论是trie树边还是AC自动机新添加的转移边)转移到了点,那么我们有:

时间复杂度:

代码

  1. #include<stdio.h>
  2. #include<iostream>
  3. #include<queue>
  4. using namespace std;
  5. const int maxn=105,maxk=4,maxl=12,mod=1000000009,maxm=1005;
  6. int n,m,tot,ans,maxlen;
  7. int chd[maxn][maxk],fail[maxn],f[maxm][maxl][maxn],dep[maxn],len[maxn];
  8. string s;
  9. queue<int>q;
  10. inline int get(char c){
  11. return c=='A'? 0:(c=='C'? 1:(c=='G'? 2:3));
  12. }
  13. void insert(string s){
  14. int now=1;
  15. for(int i=0;i<s.size();now=chd[now][get(s[i])],i++)
  16. if(chd[now][get(s[i])]==0)
  17. chd[now][get(s[i])]=++tot,dep[tot]=dep[now]+1;
  18. len[now]=dep[now];
  19. }
  20. void getfail(){
  21. while(!q.empty())
  22. q.pop();
  23. for(int i=0;i<=3;i++){
  24. if(chd[1][i])
  25. q.push(chd[1][i]),fail[chd[1][i]]=1;
  26. else chd[1][i]=1;
  27. }
  28. while(!q.empty()){
  29. int x=q.front();
  30. q.pop();
  31. for(int i=0;i<=3;i++){
  32. if(chd[x][i])
  33. q.push(chd[x][i]),fail[chd[x][i]]=chd[fail[x]][i];
  34. else chd[x][i]=chd[fail[x]][i];
  35. }
  36. len[x]=max(len[x],len[fail[x]]);
  37. }
  38. }
  39. void dp(){
  40. f[0][0][1]=1;
  41. for(int i=0;i<m;i++)
  42. for(int j=0;j<=maxlen;j++)
  43. for(int k=1;k<=tot;k++)
  44. if(f[i][j][k])
  45. for(int p=0;p<=3;p++){
  46. int x=chd[k][p];
  47. if(len[x]>j)
  48. f[i+1][0][x]=(f[i+1][0][x]+f[i][j][k])%mod;
  49. else f[i+1][j+1][x]=(f[i+1][j+1][x]+f[i][j][k])%mod;
  50. }
  51. }
  52. int main(){
  53. tot=1;
  54. scanf("%d%d",&m,&n);
  55. for(int i=1;i<=n;i++){
  56. cin>>s,maxlen=max(maxlen,(int)s.size());
  57. insert(s);
  58. }
  59. getfail();
  60. dp();
  61. for(int i=1;i<=tot;i++)
  62. ans=(ans+f[m][0][i])%mod;
  63. printf("%d\n",ans);
  64. return 0;
  65. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注