• [HNOI2006]最短母串问题 AC自动机


    题面:洛谷

    题解:

      如果我们对这些小串建出AC自动机,那么我们所求的大串就是要求满足遍历过所有AC自动机上的叶子节点,且经过步数最少的串。如果有多个步数相同的串,要输出字典序最小的串。

      在AC自动机上DP。

      因为我们要求所求串内要出现所有给定小串,而小串个数较少,因此我们考虑状压,然后保存下val[i]表示走到这个点,就可以拥有哪些子串。因为一个非终止节点也可能包含给定小串,因此我们要在建好fail之后,继承一个点x的fail[x]所包含的小串,按编号大小更新即可保证在x被更新之前fail[x]已经被更新。

      设f[i][j]表示在AC自动机的第i个节点上,状态为j的最小代价。

      不知你是否注意到这个状态里没有限制长度和字典序?

      其实这是因为我们可以用bfs解决问题,因为走任意一步的代价都是1,也就是每条边的权值都是1,所以如果我们用bfs的顺序来DP,那么谁先DP到拥有所有串,谁就是代价最小的方案。

      那么怎么保证字典序最小?

      我们只需要在bfs的时候从'a'开始枚举即可。

      

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 610
     5 #define ac 4596
     6 #define inf 2139062143
     7 
     8 int n, m, maxn, tmp, head, tail;
     9 int f[AC][ac], q1[AC * ac], q2[AC * ac];
    10 short l1[AC][ac], l2[AC][ac];
    11 int c[AC][26], val[AC], fail[AC], go[AC], tot;
    12 char s[AC];
    13 
    14 inline void add()
    15 {
    16     int len = strlen(s + 1), now = 0;
    17     for(R i = 1; i <= len; i ++)
    18     {
    19         int v = s[i] - 'A';
    20         if(!c[now][v]) c[now][v] = ++ tot, go[tot] = v;
    21         now = c[now][v];
    22     }
    23     val[now] |= tmp, tmp <<= 1;//还是要|= 的,否则要是有多个相同串在同一个节点结束就不好了,,,,
    24 }
    25 
    26 #define q q1
    27 void build()
    28 {
    29     for(R i = 0; i < 26; i ++)
    30         if(c[0][i]) q[++ tail] = c[0][i];
    31     while(head < tail)
    32     {
    33         int x = q[++ head];
    34         for(R i = 0; i < 26; i ++)
    35         {
    36             if(c[x][i]) fail[c[x][i]] = c[fail[x]][i], q[++ tail] = c[x][i];
    37             else c[x][i] = c[fail[x]][i];
    38         }
    39     }    
    40     for(R i = 1; i <= tot; i ++) val[i] |= val[fail[i]];
    41 }
    42 #undef q 
    43 
    44 void pre()
    45 {
    46     scanf("%d", &n);
    47     maxn = (1 << n) - 1, tmp = 1;
    48     memset(f, 127, sizeof(f));
    49     for(R i = 1; i <= n; i ++) scanf("%s", s + 1), add();
    50 }
    51 
    52 void bfs()
    53 {
    54     head = tail = 0;
    55     q1[++ tail] = 0, q2[tail] = 0;
    56     f[0][0] = 0;
    57     while(head < tail)
    58     {
    59         int x = q1[++ head], sta = q2[head];
    60         if(sta == maxn)
    61         {
    62             head = 0;
    63             while(x) 
    64             {
    65                 q1[++ head] = go[x];
    66                 int tmp1 = x, tmp2 = sta;
    67                 x = l1[tmp1][tmp2], sta = l2[tmp1][tmp2];
    68             }//因为要用2次,但是用了第一次之后x or sta就改变了,所以必须保存到临时变量
    69             for(R i = head; i; i --) printf("%c", q1[i] + 'A');
    70             return ;
    71         }
    72         for(R i = 0; i < 26; i ++)
    73         {
    74             int v = c[x][i], w = sta | val[v];
    75             if(f[v][w] == inf) //bfs包括了最短和字典序最小
    76             {
    77                 q1[++ tail] = v, q2[tail] = w;
    78                 f[v][w] = f[x][sta] + 1;
    79                 l1[v][w] = x, l2[v][w] = sta;
    80             }
    81         }
    82     }
    83 }
    84 
    85 int main()
    86 {
    87 //    freopen("in.in", "r", stdin);
    88     pre();
    89     build();
    90     bfs();
    91 //    fclose(stdin);
    92     return 0;
    93 }
    View Code
  • 相关阅读:
    3089:爬楼梯
    7592:求最大公约数问题
    JVM中内存回收深入分析,各种垃圾收集器
    PKU 1064 Cable master
    【面试&笔试】ASP.NET的相关问题
    1027. Colors in Mars (20) PAT
    DB_WRITER_PROCESSES与LOG_ARCHIVE_MAX_PROCESSES
    fedora下体验gentoo安装
    一个整数数组里面,除了两个数之外,其他的数字都出现了两次,写一个程序找出这两个数
    [置顶] export命令-linux
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10067726.html
Copyright © 2020-2023  润新知