虽然说这题是AC自动机吧…但是后缀数组也能解。 在这里提供一个清新的后缀数组解法。
 
后缀数组 后缀数组是个好东西啊,通过这个工具可以解决许多类型的字符串问题,这里简单介绍一下:
形式化地,对于一个长度为 $n$ 的字符串 $s$,它的形如 $s[i:n]$ 的子串被称作 $s$ 的后缀。
容易发现 $s$ 一共有 $n$ 个后缀,不妨记 $s[i:n]$ 为后缀 $i$,将所有的后缀排序后,顺序写下后缀的编号,就得到了后缀数组 (Suffix Array)。
举个例子,对 $s=ababa$,其后缀有 $a,ba,aba,baba,ababa$,排序后得到 $a,aba,ababa,ba,baba$,依次写下其编号,得到后缀数组为 $5,3,1,2,4$。
朴素求后缀数组是 $O(n^2 \log n)$ 的,这显然是不太好的。通过倍增法去求,容易发现倍增的过程是某种双关键字排序,那么对其进行基数排序,可做到 $O(n \log n)$。具体的实现超出了本篇题解的范畴,请移步 后缀排序 。
回到本题 容易发现,一个串 $s$ 若能与 $t$ 匹配,那么它必然是 $t$ 的 某个后缀的前缀 。 我们可以快速把所有后缀都排序,这样后缀就是有序的了,可以通过二分来找 $s$ 是否与 $t$ 匹配。
具体实现上,因为后缀的长度和是 $O(n^2)$ 级别的,所以不能把他们全部搞出来(会MLE)。实际上只需要写一个 cmp() 函数来比较字符串大小就好了,实现起来比较清新易懂。
复杂度因为每次要二分,所以整体多了一个 $\log$,不过均摊下来跑的非常快,常数比AC自动机大了不到一半。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <iostream>  #include <cstdio>  #include <cstring>  #include <string>  using  namespace  std ;const  int  CN = 1e6  + 6 ;char  t[CN], s[CN];string  mem[CN];int  sa[CN], rk[CN << 1 ], prk[CN << 1 ], id[CN], px[CN], cnt[CN];void  SA (int  n)  {    int  m = max(n, 300 );     for (int  i = 1 ;i <= n;i++) rk[i] = t[i - 1 ];     for (int  i = 1 ;i <= n;i++) cnt[ rk[i] ] ++;     for (int  i = 1 ;i <= m;i++) cnt[i] += cnt[i - 1 ];     for (int  i = n; i; i--) sa[ cnt[ rk[i] ]-- ] = i;     for (int  w = 1 ; w < n; w <<= 1 ){         memset (cnt, 0 , sizeof (cnt));         for (int  i = 1 ;i <= n;i++) id[i] = sa[i];         for (int  i = 1 ;i <= n;i++) cnt[ px[i] = rk[ id[i] + w ] ]++;         for (int  i = 1 ;i <= m;i++) cnt[i] += cnt[i - 1 ];         for (int  i = n; i; i--) sa[ cnt[ px[i] ]-- ] = id[i];         memset (cnt, 0 , sizeof (cnt));         for (int  i = 1 ;i <= n;i++) id[i] = sa[i];         for (int  i = 1 ;i <= n;i++) cnt[ px[i] = rk[ id[i] ] ]++;         for (int  i = 1 ;i <= m;i++) cnt[i] += cnt[i - 1 ];         for (int  i = n; i; i--) sa[ cnt[ px[i] ]-- ] = id[i];         memcpy (prk, rk, sizeof (rk)); m = 0 ;         for (int  i = 1 ;i <= n;i++)             if (prk[ sa[i] ] == prk[ sa[i - 1 ] ] && prk[ sa[i] + w ] == prk[ sa[i - 1 ] + w ])                 rk[ sa[i] ] = m;             else  rk[ sa[i] ] = ++m;         if (m == n) break ;     } } int  n, lt;inline  int  le (char  *a, char  *b, int  la,int  lb)  {    int  p = 0 ;     while (a[p] == b[p] && p < min(la, lb)) p++;     if (p == lb) return  -1 ;     if (p == la) return  true ;     return  a[p] < b[p]; } int  main ()  {                   scanf ("%d" , &n);     for (int  i = 1 ;i <= n;i++) cin  >> mem[i];     cin  >> t; lt = strlen (t);     SA(lt);          int  cnt = 0 ;     for (int  i = 1 ;i <= n;i++){         int  ls = mem[i].size();         s[ls] = '\0' ;         for (int  j = 0 ;j < ls;j++) s[j] = mem[i][j];         int  l = 1 , r = lt, m; bool  found = false ;         while (l < r){             m = (l + r) >> 1 ;             int  leq = le(t + sa[m] - 1 , s, lt - sa[m] + 1 ,ls);             if (leq == -1 ) {found = true ; break ;}             else  if (leq) l = m + 1 ;             else  r = m;         }         cnt += found ? 1  : 0 ;     }     printf ("%d" , cnt); }