分析

可以说是关于 自动机运用的较难题了。但是能用 坚决不用 自动机。我们考虑一个串作为字串出现在其它字符串中,那么就是看这个串的结束节点的 集合有多少这个区间的串。所以现在的思路就是,对所有串建一个广义后缀自动机,这里不能建那种 之后就不管的伪自动机。要使用 树建法,或者在线建法。然后对于所有等价类求出 集合,最后查询一个区间和。那么我们发现算法的瓶颈在于如何求出 集合和区间和。我们考虑线段树合并,这样总的复杂度为

  • 其实这道题用 自动机来写是非常有意思的,可以试试啊。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 410000;
    int n,m,rt[N],pos[N];
    char S[N];
    vector<int> G[N];
    int read() {
      int x = 0,f = 0;char ch = getchar();
      while(!isdigit(ch)) {if(ch == '-')f = 1;ch = getchar();}
      while(isdigit(ch)) {x = x * 10 + ch - '0';ch = getchar();}
      return f ? -x : x;
    }
    struct SAM{
      int last,size;
      struct Node{
          int len,link,nxt[26];
      }st[N<<1];
      void init() {st[0].link = -1;size++;}
      int insert(int c) {
          if(st[last].nxt[c]) {
              int p = last,q = st[p].nxt[c];
              if(st[p].len + 1 == st[q].len) return q;
              else {
                  int cl = size++;st[cl].len = st[p].len + 1;
                  st[cl].link = st[q].link;
                  for(int i = 0;i < 26;i++) st[cl].nxt[i] = st[q].nxt[i];
                  while(p != -1 && st[p].nxt[c] == q) {
                      st[p].nxt[c] = cl;p = st[p].link;
                  }
                  st[q].link = cl; return cl;
              }
          }
          int cur = size++;st[cur].len = st[last].len + 1;
          int p = last;while(p != -1 && !st[p].nxt[c]) {
              st[p].nxt[c] = cur;p = st[p].link;
          }if(p == -1) st[cur].link = 0;
          else {
              int q = st[p].nxt[c];if(st[q].len == st[p].len + 1) st[cur].link = q;
              else {
                  int cl = size++;st[cl].len = st[p].len + 1;
                  st[cl].link = st[q].link;
                  for(int i = 0;i < 26;i++) st[cl].nxt[i] = st[q].nxt[i];
                  while(p != -1 && st[p].nxt[c] == q) st[p].nxt[c] = cl,p = st[p].link;
                  st[q].link = st[cur].link = cl;
              }
          }
          return cur;
      }
    }s;
    struct  SegmentTree{
      int lc[N*20],rc[N*20],cnt[N*20];
      int size;
      void insert(int &u,int l,int r,int pos) {
          if(!u) u = ++size;cnt[u]++;if(l == r) return;
          int mid = l + r >> 1;if(pos <= mid) insert(lc[u],l,mid,pos);
          else insert(rc[u],mid+1,r,pos);
      }
      int query(int u,int l,int r,int L,int R) {
          if(!u) return 0;
          if(L <= l && r <= R) return cnt[u];
          if(l > R || r < L) return 0;int mid = l + r >> 1;
          return query(lc[u],l,mid,L,R) + query(rc[u],mid+1,r,L,R);
      }
      int merge(int a,int b,int l,int r) {
          if(!a || !b) return a|b;
          int i = ++size;
          cnt[i] = cnt[a] + cnt[b];
          if(l == r) return i;int mid = l + r >> 1;
          lc[i] = merge(lc[a],lc[b],l,mid);
          rc[i] = merge(rc[a],rc[b],mid+1,r);
          return i;
      }
    }T;
    void dfs(int x) {
      for(auto y : G[x]) {dfs(y);rt[x] = T.merge(rt[x],rt[y],1,n);}
    }
    int main() {
      n = read();m = read();s.init();
      for(int i = 1;i <= n;i++) {
          scanf("%s",S + 1);
          int len = strlen(S + 1);
          s.last = 0;for(int j = 1;j <= len;j++)
          {s.last = s.insert(S[j]-'a');T.insert(rt[s.last],1,n,i);}
          pos[i] = s.last;
      }
      for(int i = 1;i < s.size;i++) G[s.st[i].link].push_back(i);
      dfs(0);rt[0] = 0;
      while(m--) {
          int l = read(),r = read(),id = read();
          int ans = T.query(rt[pos[id]],1,n,l,r);
          printf("%d\n",ans);
      }
      return 0;
    }