[JSOI2009]密码 AC自动机

题面:洛谷

题解:

  我们对给定串建AC自动机,因为串个数较小,我们考虑状压。

  设f[i][j][k]表示走了i步,当前在j号节点上,状态为k的方案数。

  同时AC自动机上每个点的val都是对应的到达这个点后可以新增的状态。

  每次枚举下一个字符即可转移,在建完自动机之后,记得把fail树上对应的链上的点的val给继承一下。

  因为一个串已经出现了,那么就代表它的子串也已经出现了,作为它的子串,在AC自动机上就表现为跳fail可以到达的地方。

  应该我们建fail的时候已经有一个bfs序了,所以也可以直接利用这个bfs序按顺序下传val,这样可以快一点(详见代码)

  

  题目的要求是要求构建出来的串要使得所有给定串都是这个串的子串。

  

  但是在方案小于等于42时,还要求输出方案。因此我们可以记忆化搜索一下,处理出哪些状态是最终对ans产生了贡献的状态(间接贡献也算)

  然后在AC自动机上,爆搜,只走这些产生了贡献的状态,这样可以保证只搜到合法解。

  

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define LL long long
  5 #define AC 300
  6 #define ac 1050
  7 
  8 int n, m, go, maxn, tot, head, tail;
  9 int c[AC][26], fail[AC], val[AC], p[AC], q[ac];
 10 LL f[30][AC][ac], ans;//f[i][j][k]表示已经走了i步,当前在第j个节点上,状态为k的方案数
 11 char s[AC], vis[30][AC][ac];
 12 
 13 inline int read()
 14 {
 15     int x = 0;char c = getchar();
 16     while(c > '9' || c < '0') c = getchar();
 17     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
 18     return x;
 19 }
 20 
 21 inline void add()
 22 {
 23     int len = strlen(s + 1), now = 0;
 24     for(R i = 1; i <= len; i ++)
 25     {
 26         int v = s[i] - 'a';
 27         if(!c[now][v]) c[now][v] = ++ tot;
 28         now = c[now][v];
 29     }
 30     val[now] |= p[++ go];
 31 }
 32     
 33 inline void build()
 34 {
 35     for(R i = 0; i < 26; i ++) if(c[0][i]) q[++ tail] = c[0][i];
 36     while(head < tail)
 37     {
 38         int x = q[++ head];
 39         for(R i = 0; i < 26; i ++)
 40         {
 41             if(c[x][i]) fail[c[x][i]] = c[fail[x]][i], q[++ tail] = c[x][i];
 42             else c[x][i] = c[fail[x]][i];
 43         }
 44     }
 45     for(R i = 1; i <= tot; i ++) val[i] |= val[fail[i]];     
 46 }
 47     
 48 inline void DP()
 49 {
 50     f[0][0][0] = 1;
 51     //fail[8] = 14;
 52     for(R i = 0; i < n; i ++)//枚举当前走了多少步
 53         for(R j = 0; j <= tot; j ++) //枚举当前在哪个点上
 54             for(R k = 0; k <= maxn; k ++)//枚举当前状态
 55             {
 56                 if(f[i][j][k]) 
 57                     for(R l = 0; l < 26; l ++)//枚举下一个字符
 58                     {
 59                         int v = c[j][l];
 60                         f[i + 1][v][k | val[v]] += f[i][j][k];
 61                     }
 62             }
 63 }
 64 
 65 int sta[AC], top;
 66 void dfs(int x, int y, int z)//已经走了x步,当前在第y个节点上,状态为z
 67 {
 68     if(x == n) 
 69     {
 70         for(R i = 1; i <= n; i ++) printf("%c", sta[i] + 'a');
 71         puts(""); return ;
 72     }
 73     for(R i = 0; i < 26; i ++)
 74     {
 75         int v = c[y][i];
 76         if(vis[x + 1][v][z | val[v]] != '1') continue;
 77         sta[++ top] = i;
 78         dfs(x + 1, v, z | val[v]);
 79         -- top;
 80     }
 81 }
 82 
 83 inline void pre()
 84 {
 85     n = read(), m = read(), maxn = (1 << m) - 1;
 86     LL tmp = 1;
 87     for(R i = 1; i <= m; i ++) p[i] = tmp, tmp <<= 1;//要先预处理
 88     for(R i = 1; i <= m; i ++) scanf("%s", s + 1), add();
 89 }
 90 
 91 bool dfs1(int x, int y, int z)//当前在第x位, 在节点y, 状态为z
 92 {
 93     if(vis[x][y][z] == '1') return true;//1表示可以
 94     else if(vis[x][y][z] == '2') return false;//2表示不行
 95     else if(x == n) {vis[x][y][z] = '2'; return false;}
 96     for(R i = 0; i < 26; i ++)
 97     {
 98         int v = c[y][i];
 99         if(dfs1(x + 1, v, z | val[v])) vis[x][y][z] = '1';
100     }
101     if(vis[x][y][z] != '1') {vis[x][y][z] = '2'; return false;};
102     return true;
103 }
104 
105 inline void work()
106 {
107     for(R i = 0; i <= tot; i ++) 
108         if(f[n][i][maxn]) ans += f[n][i][maxn], vis[n][i][maxn] = '1';
109         else vis[n][i][maxn] = '2';
110     printf("%lld\n", ans);
111     if(ans <= 42) 
112     {
113         for(R i = 0; i < n; i ++)//枚举当前走了多少步
114             for(R j = 0; j <= tot; j ++) //枚举当前在哪个点上
115                 for(R k = 0; k <= maxn; k ++)//枚举当前状态
116                     if(f[i][j][k]) dfs1(i, j, k);
117         dfs(0, 0, 0);
118     }
119 }
120 
121 int main()
122 {
123 //    freopen("in.in", "r", stdin);
124     pre();
125     build();
126     DP();
127     work();    
128 //    fclose(stdin);
129     return 0;
130 }
View Code

 

  

posted @ 2018-12-04 21:46  ww3113306  阅读(233)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。