• 后缀自动机/回文自动机/AC自动机/序列自动机----各种自动机(自冻鸡) 题目泛做


    题目1 BZOJ 3676 APIO2014 回文串

     算法讨论:

    cnt表示回文自动机上每个结点回文串出现的次数。这是回文自动机的定义考查题。

     1 #include <cstdlib>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <iostream>
     6  
     7 using namespace std;
     8  
     9 const int C = 26;
    10 const int N = 300000 + 5;
    11 typedef long long ll;
    12  
    13 char str[N];
    14 int len;
    15 ll ans;
    16  
    17 struct State {
    18   int len, num, cnt, s, fail, next[C];
    19 }st[N];
    20  
    21 struct PTree {
    22   int n, sz, last;
    23  
    24   int newnode(int len) {
    25     st[sz].len = len;
    26     return sz ++;
    27   }
    28  
    29   void Init() {
    30     n = sz = last = 0;
    31     newnode(0); newnode(-1);
    32     st[n].s = -1;
    33     st[0].fail = 1;
    34   }
    35  
    36   int getfail(int x) {
    37     while(st[n - st[x].len - 1].s != st[n].s) x = st[x].fail;
    38     return x;
    39   }
    40  
    41   void count() {
    42     for(int i = sz - 1; i >= 0; -- i)
    43       st[st[i].fail].cnt += st[i].cnt;
    44   }
    45  
    46   void add(int c) {
    47     st[++ n].s = c;
    48     int cur = getfail(last);
    49     if(!st[cur].next[c]) {
    50       int now = newnode(st[cur].len + 2);
    51       st[now].fail = st[getfail(st[cur].fail)].next[c];
    52       st[cur].next[c] = now;
    53       st[now].num = st[st[now].fail].num + 1;
    54     }
    55     last = st[cur].next[c];
    56     st[last].cnt ++;
    57   }
    58 }ptree;
    59  
    60 int main() {
    61   scanf("%s", str);
    62   len = strlen(str);
    63   ptree.Init();
    64   for(int i = 0; i < len; ++ i)
    65     ptree.add(str[i] - 'a');
    66   ptree.count();
    67   for(int i = 0; i < ptree.sz; ++ i) {
    68     ans = max(ans, 1LL * st[i].cnt * st[i].len);
    69   }
    70   printf("%lld
    ", ans);
    71   return 0;
    72 }
    3676

    题目2 BZOJ2565 最长双回文串

    算法讨论:

    我们考虑计算出每个位置分别作为左右端点时最长回文串的长度。这样答案就是Max{r[i] + l[i + 1]}

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
     
    using namespace std;
    const int C = 26;
    const int N = 100000 + 5;
     
    struct State {
      int len, cnt, num, s, fail, next[C];
      void clear() {
        len = cnt = num = s = fail = 0;
        memset(next, 0, sizeof next);
      }
    }st[N];
     
    int len, r[N], l[N], ans;
    char str[N];
     
    struct Ptree {
      int n, sz, last;
     
      int newnode(int len) {
        st[sz].len = len;
        return sz ++;
      }
     
      void clear() {
        for(int i = 0; i <= n; ++ i)
          st[i].clear();
      }
      void Init() {
        n = sz = last = 0;
        newnode(0); newnode(-1);
        st[n].s = -1;
        st[0].fail = 1;
      }
     
      int getfail(int x) {
        while(st[n - st[x].len - 1].s != st[n].s) x = st[x].fail;
        return x;
      }
     
      void count() {
        for(int i = sz - 1; i >= 0; -- i)
          st[st[i].fail].cnt += st[i].cnt;
      }
     
      void add(int c) {
        st[++ n].s = c;
        int cur = getfail(last);
        if(!st[cur].next[c]) {
          int now = newnode(st[cur].len + 2);
          st[now].fail = st[getfail(st[cur].fail)].next[c];
          st[cur].next[c] = now;
          st[now].num = st[st[now].fail].num + 1;
        }
        last = st[cur].next[c];
        st[last].cnt ++;
      }
    }pt;
     
    int main() {
      scanf("%s", str);
      len = strlen(str);
      pt.Init();
      for(int i = 0; i < len; ++ i) {
        pt.add(str[i] - 'a');
        r[i] = st[pt.last].len;
      }
      pt.clear();
      pt.Init();
      reverse(str, str + len);
      for(int i = 0; i < len; ++ i) {
        pt.add(str[i] - 'a');
        l[len - i - 1] = st[pt.last].len;
      }
      for(int i = 0; i < len; ++ i)
        ans = max(ans, r[i] + l[i + 1]);
      printf("%d
    ", ans);
      return 0;
    }
    2565

    题目3 BZOJ2160 拉拉队排练

    算法讨论:

    首先建出回文自动机,然后排序,然后计算。话说这个题并不用这么麻烦的,好像Manacher直接就上,但是为了练习回文自动机嘛 。

     1 #include <cstdlib>
     2 #include <cstring>
     3 #include <cstdio>
     4 #include <iostream>
     5 #include <algorithm>
     6  
     7 using namespace std;
     8  
     9 const int N = 1000000 + 5;
    10 const int C = 26;
    11 const int mod = 19930726;
    12 typedef long long ll;
    13  
    14 ll k, ans = 1, cs[N];
    15 int len, rank[N];
    16 char str[N];
    17  
    18 struct State {
    19   int len, cnt, num, s, fail, next[C];
    20 }st[N];
    21  
    22 struct Ptree {
    23   int n, sz, last;
    24  
    25   int newnode(int lens) {
    26     st[sz].len = lens;
    27     return sz ++;
    28   }
    29    
    30   void Init() {
    31     sz = n = last = 0;
    32     newnode(0); newnode(-1);
    33     st[n].s = -1;
    34     st[0].fail = 1;
    35   }
    36  
    37   int getfail(int x) {
    38     while(st[n - st[x].len - 1].s != st[n].s) x = st[x].fail;
    39     return x;
    40   }
    41  
    42   void count() {
    43     for(int i = sz - 1; i >= 0; -- i)
    44       st[st[i].fail].cnt += st[i].cnt;
    45   }
    46  
    47   void add(int c) {
    48     st[++ n].s = c;
    49     int cur = getfail(last);
    50     if(!st[cur].next[c]) {
    51       int now = newnode(st[cur].len + 2);
    52       st[now].fail = st[getfail(st[cur].fail)].next[c];
    53       st[cur].next[c] = now;
    54       st[now].num = st[st[now].fail].num + 1;
    55     }
    56     last = st[cur].next[c];
    57     st[last].cnt ++;
    58   }
    59 }pt;
    60  
    61 ll quickpow(ll a, ll b) {
    62   ll res = 1;
    63   if(!b) return res;
    64   while(b) {
    65     if(b & 1) res = res * a % mod;
    66     a = a * a % mod;
    67     b >>= 1;
    68   }
    69   return res;
    70 }
    71  
    72 int main() {
    73   scanf("%d%lld", &len, &k);
    74   scanf("%s", str);
    75   pt.Init();
    76   for(int i = 0; i < len; ++ i)
    77     pt.add(str[i] - 'a');
    78   pt.count();
    79   for(int i = 0; i < pt.sz; ++ i) cs[st[i].len] ++;
    80   for(int i = 1;  i <= len; ++ i) cs[i] += cs[i - 1];
    81   for(int i = 0; i < pt.sz; ++ i) rank[cs[st[i].len] --] = i;
    82   for(int i = pt.sz - 1; i; -- i) {
    83     if(!k) break;
    84     if(st[rank[i]].len & 1) {
    85       if(k >= st[rank[i]].cnt) {
    86         ans = 1LL * ans * quickpow(st[rank[i]].len, st[rank[i]].cnt) % mod;
    87         k -= st[rank[i]].cnt;
    88       }
    89       else {
    90         ans = 1LL * ans * quickpow(st[rank[i]].len, k) % mod;
    91         k -= k;
    92       }
    93     }
    94   }
    95   printf("%lld
    ", ans % mod);
    96   return 0;
    97 }
    2160

    题目4 BZOJ2084

    算法讨论:

    首先满足条件的串对应位上的数字正好是相反的,而且一定是偶数长度的串。

    变种Manacher(其实是暴力)

     1 #include <cstdlib>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <cstdio>
     5 #include <algorithm>
     6  
     7 using namespace std;
     8  
     9 const int N = 500000 + 5;
    10 typedef long long ll;
    11  
    12 ll ans = 0;
    13 int len, Mp[N << 1];
    14 char str[N], Ma[N << 1];
    15  
    16 int idx(char c) {
    17   return c - '0';
    18 }
    19  
    20 void Manacher(char *s, int lens) {
    21   int l = 0;
    22   Ma[l ++] = '$'; Ma[l ++] = '#';
    23   for(int i = 0; i < lens; ++ i) {
    24     Ma[l ++] = s[i]; Ma[l ++] = '#';
    25   }
    26   Ma[l] = '!';
    27   int Mx = 0, id = 0;
    28   for(int i = 0; i < l; ++ i) {
    29     /*
    30     if(Mx > i) {
    31       Mp[i] = min(Mp[2 * id - i], Mx - i);
    32     }
    33     else {
    34       Mp[i] = 1;
    35     }
    36     */
    37     Mp[i] = 1;
    38     while((Ma[i - Mp[i]] == Ma[i + Mp[i]] && Ma[i - Mp[i]] == '#') || ((Ma[i - Mp[i]] != '#' && Ma[i + Mp[i]] != '#' && (idx(Ma[i - Mp[i]]) ^ idx(Ma[i + Mp[i]])) == 1))) Mp[i] ++;
    39     if(Mx < Mp[i] + i) {
    40       Mx = Mp[i] + i;
    41       id = i;
    42     }
    43   }
    44   for(int i = 1; i < l; ++ i) {
    45     if(Ma[i] == '#') {
    46       ans = (long long) ans + (Mp[i] - 1) / 2;
    47     }
    48   }
    49   printf("%lld
    ", ans);
    50 }
    51  
    52 int main() {
    53   scanf("%d%s", &len, str);
    54   Manacher(str, strlen(str));
    55    
    56   return 0;
    57 }
    1294

    题目5 Uva719 GlassBeads

    题目大意:求一个串的字典序最小的循环串。

    算法讨论:设原串长为S,把原串倍长后建立出SAM,然后在Sam上走S步,所在的位置就是最小循环串的开头。

    代码:

      1 #include <iostream>
      2 #include <algorithm>
      3 #include <cstdlib>
      4 #include <cstdio>
      5 #include <cstring>
      6 #include <cassert>
      7 using namespace std;
      8 const int L = 10000 + 5;
      9 
     10 int ans = 0;
     11 char str[L<<2];
     12 
     13 struct State{
     14   int len, pre;
     15   int next[26];
     16 
     17   State(){
     18     len = pre = 0;
     19     memset(next, 0, sizeof next);
     20   }
     21   void clear(){
     22     len = pre = 0;
     23     memset(next, 0, sizeof next);
     24   }
     25 }st[L<<2];
     26 
     27 struct SuffixAutomaton{
     28   int sz, last;
     29 
     30   void Init(){
     31     last = 0;
     32     sz = 0;
     33     for(int i = 0; i < (L<<2); ++ i)
     34       st[i].clear();
     35     st[0].len = 0; st[0].pre = -1;
     36     sz ++;
     37   }
     38   void Extend(int c){
     39     int cur = sz ++;
     40     st[cur].len = st[last].len + 1;
     41     int p;
     42 
     43     for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
     44       st[p].next[c] = cur;
     45 
     46     if(p == -1) st[cur].pre = 0;
     47     else{
     48       int q = st[p].next[c];
     49       if(st[q].len == st[p].len + 1) st[cur].pre = q;
     50       else{
     51         int cle = sz ++;
     52         st[cle].len = st[p].len + 1;
     53         st[cle].pre = st[q].pre;
     54         for(int i = 0; i < 26; ++ i) st[cle].next[i] = st[q].next[i];
     55         for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
     56           st[p].next[c] = cle;
     57         st[q].pre = st[cur].pre = cle;
     58       }
     59     }
     60     last = cur;
     61   }
     62 }SAM;
     63 
     64 void Input(){
     65   scanf("%s", str);
     66 }
     67 
     68 void Output(){
     69   printf("%d
    ", ans);
     70 }
     71 
     72 void Solve(){
     73   int len = strlen(str);
     74   SAM.Init();
     75   for(int i = 0; i < len; ++ i)
     76     str[i + len] = str[i];
     77   for(int i = 0; i < (len<<1); ++ i){
     78     SAM.Extend(str[i] - 'a');
     79   }
     80   int p = 0;
     81   for(int i = 0; i < len; ++ i){
     82     for(int j = 0; j < 26; ++ j){
     83       if(st[p].next[j]){
     84         p = st[p].next[j];
     85         break;
     86       }
     87     }
     88   }
     89 
     90   ans = st[p].len - len + 1;
     91 }
     92 
     93 int main(){
     94   int t;
     95   scanf("%d", &t);
     96   while(t --){
     97     Input();
     98     Solve();
     99     Output();
    100   }
    101   return 0;
    102 }
    UVa719

    题目6 SPOJ LCS

    题目大意:求两个串的最长公共子串。

    算法讨论:把串1建立出SAM,串2在SAM上走。如果串2的当前字符在SAM上有出边,则len++,继续向下走。如果失配,则向后缀连接跳。

    如果跳到-1,说明串1中没有这个字符,len变为0,从下一个开始走,如果有,就让Len 等于后缀连接点的Len+1,(这说明这之前的字符是可以匹配的)

    然后再向下走一步。继续。

    代码:

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdlib>
     4 #include <algorithm>
     5 #include <cstdio>
     6 
     7 using namespace std;
     8 
     9 const int N = 250000 + 5;
    10 const int C = 26;
    11 
    12 struct State {
    13   int len, pre, next[C];
    14   void clear() {
    15     len = pre = 0;
    16     memset(next, 0, sizeof next);
    17   }
    18 }st[N << 1];
    19 
    20 struct SuffixAutomaton {
    21   int sz, last;
    22 
    23   void Init() {
    24     for(int i = 0; i < (N << 1); ++ i)
    25       st[i].clear();
    26     sz = last = 1;
    27     st[sz].len = 0; st[sz].pre = -1;
    28     sz ++;
    29   }
    30 
    31   void add(int c) {
    32     int cur = sz ++, p;
    33     st[cur].len = st[last].len + 1;
    34     for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
    35       st[p].next[c] = cur;
    36     if(p == -1) st[cur].pre = 1;
    37     else {
    38       int q = st[p].next[c];
    39       if(st[q].len == st[p].len + 1) st[cur].pre = q;
    40       else {
    41         int cle = sz ++;
    42         st[cle].pre = st[q].pre;
    43         st[cle].len = st[p].len + 1;
    44         for(int i = 0; i < C; ++ i)
    45           st[cle].next[i] = st[q].next[i];
    46         for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
    47           st[p].next[c] = cle;
    48         st[q].pre = st[cur].pre = cle;
    49       }
    50     }
    51     last = cur;
    52   }
    53 }sam;
    54 
    55 char str1[N], str2[N];
    56 int len1, len2;
    57 
    58 int main() {
    59   scanf("%s%s", str1, str2);
    60   len1 = strlen(str1); len2 = strlen(str2);
    61   sam.Init();
    62   for(int i = 0; i < len1; ++ i)
    63     sam.add(str1[i] - 'a');
    64   int ans = 0, p = 1, len = 0;
    65   for(int i = 0; i < len2; ++ i) {
    66     int idx = str2[i] - 'a';
    67     if(st[p].next[idx]) {
    68       len ++;
    69       p = st[p].next[idx];
    70     }
    71     else {
    72       while(p != -1 && !st[p].next[idx])
    73         p = st[p].pre;
    74       if(p == -1) {
    75         p = 1; len = 0;
    76       }
    77       else {
    78         len = st[p].len + 1;
    79         p = st[p].next[idx]; //这句话没有写。
    80       }
    81     }
    82     ans = max(ans, len);
    83   }
    84   printf("%d
    ", ans);
    85   return 0;
    86 }
    LCS

    题目7 SPOJ LCS2

    题目大意:求多个串的最长公共子串。

    算法讨论:首先,两两之前匹配,然后取min的做法是错误的。正确的思路应该是,把其中的一个串建立SAM,然后把其它的串放在上面跑,对于SAM的每个结点,要多

    维护两个信息,一个是当前串匹配的最大值,一个是已经匹配完的串到当前点匹配的最小值,每次匹配完一个串之后,用按照拓扑序,用儿子更新父亲的最大值,让其可能的大。

    最后在所有最小值中取一个最大值,就是答案。

    代码:

      1 #include <cstdlib>
      2 #include <cstring>
      3 #include <cstdio>
      4 #include <algorithm>
      5 #include <iostream>
      6 
      7 using namespace std;
      8 
      9 const int N = 100000 + 5;
     10 const int C = 26;
     11 
     12 struct State {
     13   int pre, len, next[C], nl, ml;
     14 }st[N << 1];
     15 
     16 struct SuffixAutomaton {
     17   int sz, last;
     18 
     19   void Init() {
     20     sz = last = 1;
     21     st[sz].pre = -1; st[sz].len = 0;
     22     st[sz].ml = 0;
     23     sz ++;
     24   }
     25 
     26   void add(int c) {
     27     int cur = sz ++, p;
     28     st[cur].len = st[last].len + 1;
     29     st[cur].ml = st[last].ml + 1;
     30     for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
     31       st[p].next[c] = cur;
     32     if(p == -1) st[cur].pre = 1;
     33     else {
     34       int q = st[p].next[c];
     35       if(st[q].len == st[p].len + 1) st[cur].pre = q;
     36       else {
     37         int cle = sz ++;
     38         st[cle].pre = st[q].pre;
     39         st[cle].len = st[p].len + 1;
     40         st[cle].ml = st[p].ml + 1;
     41         for(int i = 0; i < C; ++ i) st[cle].next[i] = st[q].next[i];
     42         for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
     43           st[p].next[c] = cle;
     44         st[q].pre = st[cur].pre = cle;
     45       }
     46     }
     47     last = cur;
     48   }
     49 }sam;
     50 
     51 char str[N];
     52 int ans, lens, cs[N], rk[N << 1];
     53 
     54 int main() {
     55   int p, len;
     56   scanf("%s", str);
     57   lens = strlen(str);
     58   sam.Init();
     59   for(int i = 0; i < lens; ++ i)
     60     sam.add(str[i] - 'a');
     61   for(int i = 1; i < sam.sz; ++ i) cs[st[i].len] ++;
     62   for(int i = 1; i <= lens; ++ i) cs[i] += cs[i - 1];
     63   for(int i = 1; i < sam.sz; ++ i) rk[cs[st[i].len] --] = i;
     64   
     65   while(scanf("%s", str) != EOF) {
     66     lens = strlen(str);
     67     p = 1; len = 0;
     68     for(int i = 0; i < lens; ++ i) {
     69       int idx = str[i] - 'a';
     70       if(st[p].next[idx]) {
     71         len ++;
     72         p = st[p].next[idx];
     73       }
     74       else {
     75         while(p != -1 && !st[p].next[idx])
     76           p = st[p].pre;
     77         if(p == -1) {
     78           p = 1; len = 0;
     79         }
     80         else {
     81           len = st[p].len + 1;
     82           p = st[p].next[idx];
     83         }
     84       }
     85       st[p].nl = max(len, st[p].nl);
     86     }
     87     for(int i = sam.sz - 1; i >= 1; -- i) {
     88       st[rk[i]].ml = min(st[rk[i]].ml, st[rk[i]].nl);
     89       if(st[rk[i]].pre != -1) {
     90         st[st[rk[i]].pre].nl = max(st[st[rk[i]].pre].nl, st[rk[i]].nl);
     91       }
     92       st[rk[i]].nl = 0;
     93     }
     94   }
     95   for(int i = sam.sz - 1; i >= 1; -- i) {
     96     ans = max(ans, st[i].ml);
     97   }
     98   printf("%d
    ", ans);
     99   return 0;
    100 }
    LCS2

    题目8 SPOJ NSUBSTR

    题目大意:求一个串长度为i的子串出现次数的最大值。

    算法讨论:SAM中每个结点有两个信息维护:len, pre,len是当前点可以接收的子串的最长长度,pre是失配后的后缀连接。除此之外,还有一个我们经常说的right集合。

    这个题 就是求Right集合的大小,具体做法是,把当前串建立出SAM,然后在非复制出的节点上跑SAM,就是用原串跑原串的SAM,把跑到的结点的Right集合数++。

    接着把SAM拓扑排序,可以用基数排序,按照len从大到小处理,每个点的后缀连接点加上这个点的Right集合的大小,就是这个后缀连接点的Right集合的大小。Right集合的大小

    表示当前状态可以接收的所有子符串的出现的次数。然后我们用Right集合去更新答案,最后还要用F[i+1]去更新F[i],可是我并不知道是为什么,而且不这样做的话,交上去也能A。

    代码:

     1 #include <cstdlib>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <cstdio>
     5 #include <algorithm>
     6 
     7 using namespace std;
     8 
     9 const int N = 250000 + 5;
    10 const int C = 26;
    11 
    12 struct State {
    13   int len, pre, next[C];
    14   void clear() {
    15     len = pre = 0;
    16     memset(next, 0, sizeof next);
    17   }
    18 }st[N << 1];
    19 
    20 struct SuffixAutomaton {
    21   int sz, last;
    22 
    23   void Init() {
    24     for(int i = 0; i < (N << 1); ++ i) {
    25       st[i].clear();
    26     }
    27     sz = last = 1;
    28     st[sz].len = 0; st[sz].pre = -1;
    29     sz ++;
    30   }
    31 
    32   void add(int c) {
    33     int cur = sz ++, p;
    34     st[cur].len = st[last].len + 1;
    35     for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
    36       st[p].next[c] = cur;
    37     if(p == -1) st[cur].pre = 1;
    38     else {
    39       int q = st[p].next[c];
    40       if(st[q].len == st[p].len + 1) st[cur].pre = q;
    41       else {
    42         int cle = sz ++;
    43         st[cle].pre = st[q].pre;
    44         st[cle].len = st[p].len + 1;
    45         for(int i = 0; i < C; ++ i) st[cle].next[i] = st[q].next[i];
    46         for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
    47           st[p].next[c] = cle;
    48         st[q].pre = st[cur].pre = cle;
    49       }
    50     }
    51     last = cur;
    52   }
    53 }sam;
    54 
    55 char str[N];
    56 int len, cs[N], rk[N << 1], rt[N << 1], ans[N];
    57 
    58 int main() {
    59   int p, l;
    60   scanf("%s", str);
    61   len = strlen(str);
    62   sam.Init();
    63   for(int i = 0; i < len; ++ i) sam.add(str[i] - 'a');
    64   for(int i = 1; i < sam.sz; ++ i) cs[st[i].len] ++;
    65   for(int i = 1; i <= len; ++ i) cs[i] += cs[i - 1];
    66   for(int i = 1; i < sam.sz; ++ i) rk[cs[st[i].len] --] = i;
    67   p = 1; //以下求出了每个结点的right集合大小
    68   for(int i = 0; i < len; ++ i) {
    69     p = st[p].next[str[i] - 'a'];
    70     rt[p] ++;
    71   }
    72   for(int i = sam.sz - 1; i >= 1; -- i)
    73     rt[st[rk[i]].pre] += rt[rk[i]];
    74   for(int i = sam.sz - 1; i >= 1; -- i) {
    75     l = st[rk[i]].len;
    76     ans[l] = max(ans[l], rt[rk[i]]);
    77   }
    78   for(int i = 1; i < len; ++ i)
    79     ans[i] = max(ans[i], ans[i + 1]);
    80   for(int i = 1; i <= len; ++ i)
    81     printf("%d
    ", ans[i]);
    82   return 0;
    83 }
    NSUBSTR

    题目9 SPOJ SUBLEX

    题目大意:把一个串的所有本质不同的子串按照字典序排序后,求第k个串是什么。

    算法讨论:对原串建立自动机,然后对SAM拓扑排序,然后我们可以统计出从SAM中的每个结点开始DFS可以得到的子串数目是多少,搞出这个东西,我们就可以在SAM上走启发式的DFS了。。嘿嘿。注意这个东西可不是Right集合。两个东西有着本质的区别。

    代码:

     1 #include <cstdlib>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <cstdio>
     5 #include <algorithm>
     6 
     7 using namespace std;
     8 
     9 const int L = 90000 + 5;
    10 const int C = 26;
    11 
    12 struct State {
    13   int len, pre, next[C];
    14 }st[L << 1];
    15 
    16 struct SuffixAutomaton {
    17   int sz, last;
    18 
    19   void Init() {
    20     sz = last = 1;
    21     st[sz].pre = -1;
    22     st[sz].len = 0;
    23     sz ++;
    24   }
    25 
    26   void add(int c) {
    27     int cur = sz ++, p;
    28     st[cur].len = st[last].len + 1;
    29     for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
    30       st[p].next[c] = cur;
    31     if(p == -1) st[cur].pre = 1;
    32     else {
    33       int q = st[p].next[c];
    34       if(st[q].len == st[p].len + 1) st[cur].pre = q;
    35       else {
    36         int cle = sz ++;
    37         st[cle].pre = st[q].pre;
    38         st[cle].len = st[p].len + 1;
    39         for(int i = 0; i < C; ++ i) st[cle].next[i] = st[q].next[i];
    40         for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
    41           st[p].next[c] = cle;
    42         st[cur].pre = st[q].pre = cle;
    43       }
    44     }
    45     last = cur;
    46   }
    47 }sam;
    48 
    49 char str[L];
    50 int len, rt[L << 1], rk[L << 1], cs[L], num[L];
    51 char que[20];
    52 
    53 int main() {
    54   int p, Q, k;
    55   scanf("%s", str);
    56   len = strlen(str);
    57   sam.Init();
    58   for(int i = 0; i < len; ++ i)
    59     sam.add(str[i] - 'a');
    60   for(int i = 1; i < sam.sz; ++ i) cs[st[i].len] ++;
    61   for(int i = 1; i <= len; ++ i) cs[i] += cs[i - 1];
    62   for(int i = 1; i < sam.sz; ++ i) rk[cs[st[i].len] --] = i;
    63   p = 1;
    64   for(int i = 0; i < len; ++ i) {
    65     p = st[p].next[str[i] - 'a'];
    66     rt[p] ++;
    67   }
    68   for(int i = sam.sz - 1; i >= 1; -- i)
    69     rt[st[rk[i]].pre] += rt[rk[i]];
    70   for(int i = sam.sz - 1; i >= 1; -- i)
    71     num[i] = 1;
    72   for(int i = sam.sz - 1; i >= 1; -- i) {
    73     for(int j = 0; j < C; ++ j) {
    74       num[rk[i]] += num[st[rk[i]].next[j]];
    75     }
    76   }
    77   scanf("%d", &Q);
    78   while(Q --) {
    79     scanf("%d", &k);
    80     p = 1;
    81     while(k) {
    82       for(int j = 0; j < C; ++ j) {
    83         if(num[st[p].next[j]] >= k) {
    84           putchar(j + 'a');
    85           k --;
    86           p = st[p].next[j];
    87           break;
    88         }
    89         else {
    90           k -= num[st[p].next[j]];
    91         }
    92       }
    93     }
    94     puts("");
    95   }
    96   return 0;
    97 }
    SUBLEX

    题目10 HDU 4436 string2int

    题目大意:给出n个串,求这n个串所有不同子串组成的所有十进制数的和是多少,有前导0的去掉前导0,出现多次的只算一次,答案对2012取模。

    算法讨论:多串建立自动机。中间用特殊的字符隔开。然后拓扑排序,从len由小到大处理。这个题自己是又T又Wa,并不知道是为什么。还要再思考思考。

    代码:

     1 #include <cstdlib>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <cstdio>
     6 
     7 using namespace std;
     8 
     9 const int C = 26;
    10 const int N = 110000 + 5;
    11 typedef long long ll;
    12 
    13 struct State {
    14   int pre, len, next[C];
    15   void clear() {
    16     pre = len = 0;
    17     memset(next, 0, sizeof next);
    18   }
    19 }st[N << 1];
    20 
    21 struct SuffixAutomaton {
    22   int sz, last;
    23 
    24   void Init() {
    25     for(int i = 0; i < sz; ++ i) st[i].clear();
    26     sz = last = 1;
    27     st[sz].pre = -1; st[sz].len = 0;
    28     sz ++;
    29   }
    30 
    31   void add(int c) {
    32     int cur = sz ++, p;
    33     st[cur].len = st[last].len + 1;
    34     for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
    35       st[p].next[c] = cur;
    36     if(p == -1) st[cur].pre = 1;
    37     else {
    38       int q = st[p].next[c];
    39       if(st[q].len == st[p].len + 1) st[cur].pre = q;
    40       else {
    41         int cle = sz ++;
    42         st[cle].pre = st[q].pre;
    43         st[cle].len = st[p].len + 1;
    44         for(int i = 0; i < C; ++ i) st[cle].next[i] = st[q].next[i];
    45         for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
    46           st[p].next[c] = cle;
    47         st[q].pre = st[cur].pre = cle;
    48       }
    49     }
    50     last = cur;
    51   }
    52 }sam;
    53 
    54 char str[N];
    55 int lens, n, zz, ans, cr, p, Len;
    56 int rk[N << 1], cs[N], sum[N << 1], cnt[N << 1];
    57 
    58 int main() {
    59   while(scanf("%d", &n) != EOF) {
    60     memset(sum, 0, sizeof sum);
    61     memset(cnt, 0, sizeof cnt);
    62     memset(cs, 0, sizeof cs);
    63     memset(rk, 0, sizeof rk);
    64     sam.Init(); Len = 0;
    65     while(n --) {
    66       scanf("%s", str);
    67       lens = strlen(str);
    68       Len += lens;
    69       for(int i = 0; i < lens; ++ i)
    70         sam.add(str[i] - '0');
    71       sam.add('?' - '0');
    72       Len += 1;
    73     }
    74     ans = 0;
    75     cnt[1] = 1;
    76     for(int i = 1; i < sam.sz; ++ i) cs[st[i].len] ++;
    77     for(int i = 1; i <= Len; ++ i) cs[i] += cs[i - 1];
    78     for(int i = 1; i < sam.sz; ++ i) rk[cs[st[i].len] --] = i;
    79     for(int i = 1; i < sam.sz; ++ i) {
    80       p = rk[i];
    81       for(int j = 0; j < 10; ++ j) {
    82         if(p == 1 && !j) continue;
    83         if(st[p].next[j]) {
    84           cr = st[p].next[j];
    85           cnt[cr] = (cnt[cr] + cnt[p]) % 2012;
    86           sum[cr] = (sum[cr] + sum[p] * 10 + cnt[p] * j) % 2012;
    87         }
    88       }
    89       ans = (ans + sum[p]) % 2012;
    90     }
    91     printf("%d
    ", ans);
    92   }
    93   return 0;
    94 }
    HDU 4436

    题目11 BZOJ3998 TJOI 弦论 

    题目大意:把一个串的所有子串按照字典序排序,求第K个。不同的是,如果type = 1,则位置不同的相同的串是不同的串,如果type = 0,则位置不同的相同的串算一个。

    算法讨论:对于type = 0则是自动机的长处。对于type = 1的也是自动机的长处,对于type = 0,每次我们让k --就好了,对于type = 1,我们每次让k 减去当前这个right集合的

    大小,对于从某个点出发可以得到多少个子串,type = 0,就把num[i]的初始设为1,对于type = 1,就把num[i]的初始设为其right集合的大小 。然后正常计算就可以。

    代码:

      1 #include <cstdlib>
      2 #include <iostream>
      3 #include <algorithm>
      4 #include <cstdio>
      5 #include <cstring>
      6  
      7 using namespace std;
      8  
      9 const int N = 500000 + 5;
     10 const int C = 26;
     11  
     12 struct State {
     13   int pre, len, next[C];
     14 }st[N << 1];
     15  
     16 char str[N];
     17 int type, k, len;
     18 int cs[N], rk[N << 1], num[N << 1], rt[N << 1];
     19  
     20 struct SuffixAutomaton {
     21   int sz, last;
     22  
     23   void Init() {
     24     sz = last = 1;
     25     st[sz].pre = -1; st[sz].len = 0;
     26     sz ++;
     27   }
     28  
     29   void add(int c) {
     30     int cur = sz ++, p;
     31     st[cur].len = st[last].len + 1;
     32     for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
     33       st[p].next[c] = cur;
     34     if(p == -1) st[cur].pre = 1;
     35     else {
     36       int q = st[p].next[c];
     37       if(st[q].len == st[p].len + 1) st[cur].pre = q;
     38       else {
     39         int cle = sz ++;
     40         st[cle].len = st[p].len + 1;
     41         st[cle].pre = st[q].pre;
     42         for(int i = 0; i < C; ++ i) st[cle].next[i] = st[q].next[i];
     43         for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
     44           st[p].next[c] = cle;
     45         st[q].pre = st[cur].pre = cle;
     46       }
     47     }
     48     last = cur;
     49   }
     50 }sam;
     51  
     52 int main() {
     53   int p;
     54   scanf("%s", str);
     55   scanf("%d%d", &type, &k);
     56   len = strlen(str);
     57   sam.Init();
     58   for(int i = 0; i < len; ++ i)
     59     sam.add(str[i] - 'a');
     60   for(int i = 1; i < sam.sz; ++ i) cs[st[i].len] ++;
     61   for(int i = 1; i <= len; ++ i) cs[i] += cs[i - 1];
     62   for(int i = 1; i < sam.sz; ++ i) rk[cs[st[i].len] --] = i;
     63   p = 1;//求Right
     64   for(int i = 0; i < len; ++ i) {
     65     p = st[p].next[str[i] - 'a'];
     66     rt[p] ++;
     67   }
     68   for(int i = sam.sz - 1; i >= 1; -- i)
     69     rt[st[rk[i]].pre] += rt[rk[i]];
     70   //求num
     71   if(type)
     72     for(int i = sam.sz - 1; i >= 1; -- i)
     73       num[rk[i]] = rt[rk[i]];
     74   else
     75     for(int i = sam.sz - 1; i >= 1; -- i)
     76       num[i] = 1;
     77   num[1] = 0; rt[1] = 0;
     78   for(int i = sam.sz - 1; i >= 1; -- i) {
     79     for(int j = 0; j < C; ++ j) {
     80       num[rk[i]] += num[st[rk[i]].next[j]];
     81     }
     82   }
     83   if(num[1] < k) { puts("-1"); return 0; }
     84   p = 1;
     85   while(k) {
     86     for(int j = 0; j < C; ++ j) {
     87       if(num[st[p].next[j]] >= k) {
     88         if(type) k -= rt[st[p].next[j]];
     89         else k --;
     90         p = st[p].next[j];
     91         putchar(j + 'a');
     92         break;
     93       }
     94       else {
     95         k -= num[st[p].next[j]];
     96       }
     97     }
     98   }
     99   return 0;
    100 }
    BZOj 3998

    题目12 BZOJ 2555 Subtrings

    题目大意:对于一个字符串,有两个操作,一个是向当前串尾部加入一个字符串,另一个操作是询问某个串出现了多少次。强制在线。

    算法讨论:首先,求某个串出现了多少次就是把这个串放在自动机上去跑,然后跑到的end结点的Right集合大小就是这个串出现的次数。

    但是本题要求还有添加字符,我们不可能每一次都重新计算一下Right集合,所以我们要有一个支持在线修改Right并维护其大小的东西。

    由于这东西有很强的树型结构,所以我们采用LInkCutTree来维护它。每次加入一个字符,就把这个字符的Right集合大小置为1.然后有设置pre指针的地方我们就Link,

    如果有改变pre指针的地方我们就先Cut再Link,最后把某个串放在上面跑,输出end结点的val值就可以了。

    这个题会了两个东西:

    1、在Extend的时候求哪些点是有用的点(不是复制出来的点),我原来求Right集合的有用点都是先建立SAM,再把原串放在上面跑,求有用的结点。

    LinkCutTree在Link和Cut的时候就可以维护Right大小了。对于第一种方法,我们还要拓扑一下求Right集合大小。

    2、用LinkCutTree维护Right集合,有一个好处是Splay中没有pushup。

    代码:

      1 #include <iostream>
      2 #include <cstring>
      3 #include <cstdlib>
      4 #include <cstdio>
      5 #include <algorithm>
      6 
      7 using namespace std;
      8 
      9 const int N = 600000 + 5;
     10 const int C = 26;
     11 
     12 int sz, last, lens, n, top;
     13 char str[N], ss[10];
     14 int fa[N << 1], c[N << 1][2], val[N << 1], add[N << 1], sta[N << 1];
     15 bool rev[N << 1];
     16 
     17 struct State {
     18   int len, pre, next[C];
     19 }st[N << 1];
     20 
     21 void pushdown(int x) {
     22   int l = c[x][0], r = c[x][1];
     23   if(rev[x]) {
     24     rev[x] ^= 1; rev[l] ^= 1; rev[r] ^= 1;
     25     swap(c[x][0], c[x][1]);
     26   }
     27   if(add[x]) {
     28     val[l] += add[x]; val[r] += add[x];
     29     add[l] += add[x]; add[r] += add[x];
     30     add[x] = 0;
     31   }
     32 }
     33 
     34 bool isroot(int x) {
     35   return c[fa[x]][0] != x && c[fa[x]][1] != x;
     36 }
     37 
     38 void rotate(int x) {
     39   int y = fa[x], z = fa[y], l, r;
     40   l = (c[y][0] == x) ^ 1; r = l ^ 1;
     41   if(!isroot(y)) {
     42     c[z][(c[z][0] == y) ^ 1] = x;
     43   }
     44   fa[x] = z; fa[y] = x; fa[c[x][r]] = y;
     45   c[y][l] = c[x][r]; c[x][r] = y;
     46 }
     47 
     48 void splay(int x) {
     49   top = 0; sta[++ top] = x;
     50   for(int i = x; !isroot(i); i = fa[i])
     51     sta[++ top] = fa[i];
     52   while(top) pushdown(sta[top --]);
     53   while(!isroot(x)) {
     54     int y = fa[x], z = fa[y];
     55     if(!isroot(y)) {
     56       if((c[z][0] == y) ^ (c[y][0] == x)) rotate(x);
     57       else rotate(y);
     58     }
     59     rotate(x);
     60   }
     61 }
     62 
     63 void Access(int x) {
     64   int t = 0;
     65   while(x) {
     66     splay(x); c[x][1] = t;
     67     t = x; x = fa[x];
     68   }
     69 }
     70 
     71 int Findroot(int x) {
     72   Access(x); splay(x);
     73   while(c[x][0]) x = c[x][0];
     74   return x;
     75 }
     76 
     77 void Makeroot(int x) {
     78   Access(x); splay(x); rev[x] ^= 1;    
     79 }
     80 
     81 void Cut(int x) {
     82   Access(x); splay(x);
     83   val[c[x][0]] -= val[x];
     84   add[c[x][0]] -= val[x];
     85   c[x][0] = fa[c[x][0]] = 0;
     86 }
     87 
     88 void Link(int x, int y) {
     89   fa[x] = y;
     90   Access(y); splay(y);
     91   val[y] += val[x]; add[y] += val[x];
     92 }
     93 
     94 void decode(char *s, int mask) {
     95   int le = strlen(s);
     96   for(int j = 0; j < le; ++ j) {
     97     mask = (mask * 131 + j) % le;
     98     swap(s[mask], s[j]);
     99   }
    100 }
    101 
    102 void Init() {
    103   sz = last = 1;
    104   st[sz].pre = -1; st[sz].len = 0;
    105   sz ++;
    106 }
    107 
    108 void Extend(int c) {
    109   int cur = sz ++, p;
    110   st[cur].len = st[last].len + 1;
    111   val[cur] = 1;
    112   for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
    113     st[p].next[c] = cur;
    114   if(p == -1) st[cur].pre = 1, Link(cur, 1);
    115   else {
    116     int q = st[p].next[c];
    117     if(st[q].len == st[p].len + 1) st[cur].pre = q, Link(cur, q);
    118     else {
    119       int cle = sz ++;
    120       st[cle].pre = st[q].pre; Link(cle, st[q].pre);
    121       st[cle].len = st[p].len + 1;
    122       for(int i = 0; i < C; ++ i) st[cle].next[i] = st[q].next[i];
    123       for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
    124         st[p].next[c] = cle;
    125       Cut(q); Link(q, cle); Link(cur, cle);
    126       st[q].pre = st[cur].pre = cle;
    127     }
    128   }
    129   last = cur;
    130 }
    131 
    132 int main() {
    133   int mask = 0;
    134   scanf("%d", &n);
    135   scanf("%s", ss);
    136   lens = strlen(ss);
    137   Init();
    138   for(int i = 0; i < lens; ++ i)
    139     Extend(ss[i] - 'A');
    140   while(n --) {
    141     scanf("%s%s", ss, str);
    142     lens = strlen(str);
    143     if(ss[0] == 'A') {
    144       decode(str, mask);
    145       for(int i = 0; i < lens; ++ i)
    146         Extend(str[i] - 'A');
    147     }
    148     else if(ss[0] == 'Q') {
    149       decode(str, mask);
    150       int p = 1; bool flag = false;
    151       for(int i = 0; i < lens; ++ i) {
    152         if(!st[p].next[str[i] - 'A']) {
    153           flag = true; break;
    154         }
    155         p = st[p].next[str[i] - 'A'];
    156       }
    157       if(flag) {
    158         puts("0"); continue;
    159       }
    160       splay(p);
    161       printf("%d
    ", val[p]);
    162       mask ^= val[p];
    163     }
    164   }
    165   return 0;
    166 }
    2555

    题目13 HDU 4622

    题目大意:

    给一个串,询问l..r的不同子串个数。

    算法讨论:

    新加入一个字符,新增的不同子串数为st[cur].len - st[st[cut].pre].len。

    对于询问,我们离线操作,左端点相同的我们只维护一个加字符,如果左端点不同,就定期重建自动机。

    动态维护不同子串数,SMZ大神自己YY了一个做法,考虑加入一个结点会对多少条从根结点到当前结点的路径造成影响。

    代码以SDOI2016的某个水题附上。

    代码:

     1 #include <cstdlib>
     2 #include <iostream>
     3 #include <algorithm>
     4 #include <cstring>
     5 #include <cstdio>
     6 
     7 using namespace std;
     8 
     9 const int N = 2000 + 5;
    10 const int C = 26;
    11 
    12 int Ans = 0;
    13 
    14 struct State {
    15   int pre, len, next[C];
    16   void clear() {
    17     pre = len = 0;
    18     memset(next, 0, sizeof next);
    19   }
    20 }st[N << 1];
    21 
    22 struct SuffixAutomaton {
    23   int sz, last;
    24 
    25   void Init() {
    26     for(int i = 0; i < sz; ++ i)
    27       st[i].clear();
    28     sz = last = 1;
    29     st[sz].len = 0; st[sz].pre = -1;
    30     sz ++;
    31   }
    32 
    33   void add(int c) {
    34     int cur = sz ++, p;
    35     st[cur].len = st[last].len + 1;
    36     for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
    37       st[p].next[c] = cur;
    38     if(p == -1) st[cur].pre = 1;
    39     else {
    40       int q = st[p].next[c];
    41       if(st[q].len == st[p].len + 1) st[cur].pre = q;
    42       else {
    43         int cle = sz ++;
    44         st[cle].pre = st[q].pre;
    45         st[cle].len = st[p].len + 1;
    46         for(int i = 0; i < C; ++ i) st[cle].next[i] = st[q].next[i];
    47         for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
    48           st[p].next[c] = cle;
    49         st[q].pre = st[cur].pre = cle;
    50       }
    51     }
    52     last = cur;
    53     Ans = Ans + st[cur].len - st[st[cur].pre].len;
    54   }
    55 }sam;
    56 
    57 struct Query {
    58   int l, r, id;
    59   bool operator < (const Query &k) const {
    60     if(l == k.l) return r < k.r;
    61     return l < k.l;
    62   }
    63 }qs[10005];
    64 
    65 int ans[10005], cs[N], rk[N << 1], num[N << 1];
    66 char str[N];
    67 
    68 int main() {
    69   int t, Q;
    70   scanf("%d", &t);
    71   while(t --) {
    72     scanf("%s", str + 1);
    73     scanf("%d", &Q);
    74     for(int i = 1; i <= Q; ++ i) {
    75       scanf("%d%d", &qs[i].l, &qs[i].r);
    76       qs[i].id = i;
    77     }
    78     sort(qs + 1, qs + Q + 1);
    79     for(int i = 1; i <= Q; ++ i) {
    80       if(qs[i].l == qs[i - 1].l) {
    81         for(int j = qs[i - 1].r + 1; j <= qs[i].r; ++ j)
    82           sam.add(str[j] - 'a');
    83       }
    84       else {
    85         sam.Init(); Ans = 0;
    86         for(int j = qs[i].l; j <= qs[i].r; ++ j)
    87           sam.add(str[j] - 'a');
    88       }
    89       ans[qs[i].id] = Ans;
    90     }
    91     for(int i = 1; i <= Q; ++ i)
    92       printf("%d
    ", ans[i]);
    93   }
    94   return 0;
    95 }
    4622

    附SMZ大神的另一个做法:SDOI2016 生成魔咒她的代码:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <map>
     6 #define maxn 200010
     7 using namespace std;
     8 
     9 int n;
    10 
    11 int a[maxn];
    12 
    13 struct Node{
    14     int len, link;
    15     map<int, int> nxt;
    16 }st[maxn];
    17 
    18 int root, size, last;
    19 void init(){
    20     root = size = last = 0;
    21     st[root].len = 0;
    22     st[root].link = -1;
    23 }
    24 
    25 long long ans;
    26 
    27 long long vis[maxn];
    28 
    29 void Extend(int c){
    30     int cur = ++ size, p = last;
    31     st[cur].len = st[p].len + 1;
    32     for(; ~p && st[p].nxt[c] == 0; p = st[p].link)
    33         st[p].nxt[c] = cur, vis[cur] += vis[p];
    34     ans += vis[cur];
    35     if(p == -1)
    36         st[cur].link = root;
    37     else{
    38         int q = st[p].nxt[c];
    39         if(st[q].len == st[p].len + 1)
    40             st[cur].link = q;
    41         else{
    42             int clone = ++ size;
    43             st[clone] = st[q];
    44             st[clone].len = st[p].len + 1;
    45             vis[clone] = 0;
    46             for(; ~p && st[p].nxt[c] == q; p = st[p].link)
    47                 st[p].nxt[c] = clone, vis[clone] += vis[p], vis[q] -= vis[p];
    48             st[q].link = st[cur].link = clone;
    49         }
    50     }
    51     last = cur;
    52 }
    53 
    54 
    55 int main(){
    56     freopen("menci_incantation.in", "r", stdin);
    57     freopen("menci_incantation.out", "w", stdout);
    58     init();
    59     vis[root] = 1;
    60     scanf("%d", &n);
    61     for(int i = 1; i <= n; i ++){
    62         scanf("%d", &a[i]);
    63         Extend(a[i]);
    64         printf("%lld
    ", ans);
    65     }
    66     return 0;
    67 }
    SDOI 2016

    题目14 百度之星2015年复赛 最强密码(提交入口:HDU 5262)

    题目大意:

    给定一个字符串,求最短的不是其子序列的串的长度和个数。

    算法讨论:

    序列自动机 + Dp。由于答案只在空节点进行累计,所以我们建立一个空的专门接受没有这个字符出边的节点,

    然后在这个节点上进行答案统计就可以,我们从前向后走序列自动机,第一个更新这个节点长度的就是最短串的长度,

    后面的节点如果走到这个节点,由于长度都比这个大,所以只会对其进行答案累计。

    代码:

    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    
    using namespace std;
    const int N = 100000 + 5;
    const int C = 26;
    const int mod = 1e9 + 7;
    
    int len;
    char str[N];
    int last[N], anslen[N], dp[N];
    struct Node {
      int next[C];
    }st[N];
    
    int main() {
      int count = 0, T;
      scanf("%d", &T);
      while(T --) {
    	++ count;
    	printf("Case #%d:
    ", count);
    	scanf("%s", str + 1);
    	len = strlen(str + 1);
    	for(int i = 0; i < C; ++ i) last[i] = len + 1;
    	for(int i = len; i >= 1; -- i) {
    	  for(int j = 0; j < C; ++ j) st[i].next[j] = last[j];
    	  last[str[i] - 'a'] = i;
    	}
    	for(int i = 0; i < C; ++ i) st[0].next[i] = last[i];
    	for(int i = 1; i <= len + 1; ++ i) anslen[i] = len + 1;
    	anslen[0] = 0; dp[0] = 1;
    	for(int i = 0; i <= len; ++ i) {
    	  for(int j = 0; j < C; ++ j) {
    		int nxt = st[i].next[j];
    		if(anslen[i] + 1 < anslen[nxt]) {
    		  anslen[nxt] = anslen[i] + 1;
    		  dp[nxt] = dp[i];
    		}
    		else if(anslen[i] + 1 == anslen[nxt]) {
    		  dp[nxt] += dp[i];
    		  if(dp[nxt] >= mod) dp[nxt] -= mod;
    		}
    	  }
    	}
    	printf("%d %d
    ", anslen[len + 1], dp[len + 1]);
      }
      return 0;
    }
    

    题目15 HDU 3336

    题目大意:

    求一个串的所有前缀在这个串中的出现次数和,答案Mod10007

    算法讨论:AC自动机

    对于每个节点都是一个end结点,所以我们只要在原串的AC自动机上跑原串,像以往那样正常统计出现次数就可以了。

    #include <cstdlib>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    
    using namespace std;
    const int N = 200000 + 5;
    const int C = 26;
    const int mod = 10007;
    typedef long long ll;
    
    char str[N];
    int n;
    ll ans = 0;
    
    struct Ac {
      int sz;
      int c[N][C], fail[N], end[N];
    
      Ac() {
    	sz = 1;
      }
      void clear() {
    	sz = 1;
    	memset(c, 0, sizeof c);
    	memset(fail, 0, sizeof fail);
    	memset(end, 0, sizeof end);
      }
    
      int idx(char cc) { return cc - 'a'; }
      void insert(char *buf) {
    	int cur = 0, len = strlen(buf);
    	for(int i = 0; i < len; ++ i) {
    	  int x = idx(buf[i]);
    	  if(!c[cur][x]) c[cur][x] = sz ++;
    	  cur = c[cur][x];
    	  end[cur] ++;
    	}
      }
      void build() {
    	queue <int> q;
    	fail[0] = 0;
    	for(int i = 0; i < C; ++ i)
    	  if(!c[0][i]) c[0][i] = 0;
    	  else {
    		fail[c[0][i]] = 0;
    		q.push(c[0][i]);
    	  }
    	while(!q.empty()) {
    	  int x = q.front(); q.pop();
    	  for(int i = 0; i < C; ++ i) {
    		if(!c[x][i]) c[x][i] = c[fail[x]][i];
    		else {
    		  fail[c[x][i]] = c[fail[x]][i];
    		  q.push(c[x][i]);
    		}
    	  }
    	}
      }
      void query(char *buf) {
    	int cur = 0, len = strlen(buf);
    	for(int i = 0; i < len; ++ i) {
    	  int x = idx(buf[i]);
    	  cur = c[cur][x];
    	  int tmp = cur;
    	  while(tmp) {
    		if(end[tmp]) {
    		  ans = ans + end[tmp];
    		  if(ans >= mod) {
    			ans -= mod;
    		  }
    		}
    		tmp = fail[tmp];
    	  }
    	}
      }
    }ac;
    
    int main() {
      int t;
      scanf("%d", &t);
      while(t --) {
    	scanf("%d", &n);
    	scanf("%s", str);
    	ac.insert(str);
    	ac.build();
    	ac.query(str);
    	printf("%lld
    ", (ans + mod) % mod);
    	ans = 0;
    	ac.clear();
      }
      return 0;
    }
    

    题目16 BZOJ 2780

    题目大意:

    求一个串在多少个串中出现。

    算法讨论:

    后缀自动机。对多串建立广义后缀自动机,每个节点都要保存其在哪个串中出现的颜色信息。然后建立出Parent树,求出DFS序。

    对于每个询问, 在自动机上先跑一遍,求出其到达的end结点,没有的就直接把答案设为0.

    然后将询问全部离线,按照DFS右端点从小到大排序,然后再按照DFS序的顺序处理自动机上的每个结点,

    这里用树状数组来维护区间和进行统计答案。有点莫队的思想。计算即可。

    代码:

    #include <cstdlib>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
     
    using namespace std;
    const int N = 100000 + 5;
    const int C = 128;
     
    struct State {
      int len, pre, next[C];
      vector <int> color;
    }st[N << 1];
     
    struct SuffixAutomaton {
      int sz, last;
      void Init() {
        last = sz = 1;
        st[sz].pre = -1; st[sz].len = 0;
        sz ++;
      }
      void add(int c, int ccc) {
        int cur = sz ++, p;
        st[cur].len = st[last].len + 1;
        for(p = last; p != -1 && !st[p].next[c]; p = st[p].pre)
          st[p].next[c] = cur;
        if(p == -1) st[cur].pre = 1;
        else {
          int q = st[p].next[c];
          if(st[q].len == st[p].len + 1) st[cur].pre = q;
          else {
            int cle = sz ++;
            st[cle].pre = st[q].pre;
            st[cle].len = st[p].len + 1;
            for(int i = 0; i < C; ++ i) st[cle].next[i] = st[q].next[i];
            for(; p != -1 && st[p].next[c] == q; p = st[p].pre)
              st[p].next[c] = cle;
            st[q].pre = st[cur].pre = cle;
          }
        }
        last = cur;
        st[cur].color.push_back(ccc);
      }
    }sam;
     
    char str[N];
    int n, m, cnt, tim, len;
    int head[N << 1], in[N << 1], out[N << 1], lastins[N];
    int ans[N], seq[N << 1], cc[N << 1];
    struct Edge {
      int from, to, next;
    }edges[N << 1];
    struct Query {
      int id, l, r;
      bool operator < (const Query &k) const {
        return r < k.r;
      }
    }Q[N];
     
    void insert(int f, int u) {
      ++ cnt;
      edges[cnt].from = f; edges[cnt].to = u;
      edges[cnt].next = head[f]; head[f] = cnt;
    }
    void dfs(int u) {
      in[u] = ++ tim; seq[tim] = u;
      for(int i = head[u]; i; i = edges[i].next)
        dfs(edges[i].to);
      out[u] = tim;
    }
     
    int LowBit(int x) { return x & (-x); }
    int query(int x) {
      int res = 0;
      for(int i = x; i > 0; i -= LowBit(i)) res += cc[i];
      return res;
    }
    void update(int x, int v) {
      for(int i = x; i < sam.sz; i += LowBit(i)) cc[i] += v;
    }
     
    int main() {
      scanf("%d%d", &n, &m);
      sam.Init();
      for(int i = 1; i <= n; ++ i) {
        scanf("%s", str);
        len = strlen(str);
        for(int j = 0; j < len; ++ j)
          sam.add(str[j], i);
        sam.last = 1;//back to the root ....
      }
      for(int i = 1; i < sam.sz; ++ i)
        if(st[i].pre != -1) insert(st[i].pre, i);
      dfs(1);
      for(int i = 1; i <= m; ++ i) {
        scanf("%s", str);
        len = strlen(str);
        int p = 1; bool flag = false;
        for(int j = 0; j < len; ++ j) {
          if(!st[p].next[(int)str[j]]) { flag = true; break; }
          else p = st[p].next[(int)str[j]];
        }
        Q[i].id = i;
        if(flag) { Q[i].l = Q[i].r = -1; }
        else { Q[i].l = in[p]; Q[i].r = out[p]; }
      }
      sort(Q + 1, Q + m + 1);
      int cur = 1;
      while(cur <= m && Q[cur].l == -1) ans[Q[cur].id] = 0, cur ++;
      for(int i = 1; i < sam.sz; ++ i) {
        for(int j = 0; j < (signed) st[seq[i]].color.size(); ++ j) {
          int now = st[seq[i]].color[j];
          update(i, 1);
          if(lastins[now]) update(lastins[now], -1);
          lastins[now] = i;
        }
        for(; Q[cur].r == i; ++ cur) ans[Q[cur].id] = query(Q[cur].r) - query(Q[cur].l - 1);
      }
      for(int i = 1; i <= m; ++ i) printf("%d
    ", ans[i]);
      return 0;
    }
    
  • 相关阅读:
    浅谈值对象
    循环一个节点列表(NodeList)或者数组,并且绑定事件处理函数引发对闭包的理解
    当前窗口和Iframe之间的相互访问(图片上传成功后立刻显示在当前页面上)
    网页动态加载图片 通过JS和jquery实现。
    javascript拖动层函数封装
    javascript中变量声明提升(Hoisting)
    运动框架必备的运动算法 留着用!
    CSS3特性之改变在浏览器上选中文字时,默认的背景颜色和文字颜色
    仿淘宝商品图片放大镜效果(鼠标移动上去会出现放大的图片,并且可以移动)
    与PHP交互中文编码的几个函数 decodeURIComponent,encodeURIComponent,encodeURI,decodeURI
  • 原文地址:https://www.cnblogs.com/sxprovence/p/5285012.html
Copyright © 2020-2023  润新知