【BZOJ5417】【UOJ395】【NOI2018】你的名字

【题目链接】

【思路要点】

  • S S 用后缀自动机建后缀树,并在其 DFS 序上建立主席树。
  • 考虑一个询问 (T,ql,qr) ( T , q l , q r ) ,从后到前依次考虑其每一个后缀对答案的贡献,显然对于每一个后缀 i i ,存在一个长度 len ,当且仅当 j>i+len j > i + l e n T[i...j] T [ i . . . j ] 会对答案产生贡献。
  • S S 的后缀自动机上匹配 T 的每一个后缀,求出对于每一个 i i ,最大的 j 使得 T[i...j] T [ i . . . j ] S[ql...qr] S [ q l . . . q r ] 中出现过,则 len=Max{ji+1,lcp(i,i+1),lcp(i,i+2),...,lcp(i,|T|)} l e n = M a x { j − i + 1 , l c p ( i , i + 1 ) , l c p ( i , i + 2 ) , . . . , l c p ( i , | T | ) } ,其中 lcp(i,j) l c p ( i , j ) 表示 T[i...|T|] T [ i . . . | T | ] T[j...|T|] T [ j . . . | T | ] LCP L C P
  • 再借助后缀数组求出 Max{lcp(i,i+1),lcp(i,i+2),...,lcp(i,|T|)} M a x { l c p ( i , i + 1 ) , l c p ( i , i + 2 ) , . . . , l c p ( i , | T | ) } 即可回答询问。
  • 时间复杂度 O(|S|Log|S|+|T|(Log|T|+Log|S|)) O ( | S | L o g | S | + ∑ | T | ∗ ( L o g | T | + L o g | S | ) )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXLOG = 20;
const int MAXC = 256;
const int MAXN = 5e5 + 5;
const int MAXP = 1e6 + 5;
const int SEGP = 2e7 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
struct SegmentTree {
  struct Node {
      int lc, rc;
      int sum;
  } a[SEGP];
  int n, size, root[MAXN];
  void build(int &root, int l, int r) {
      if (root == 0) root = ++size;
      if (l == r) return;
      int mid = (l + r) / 2;
      build(a[root].lc, l, mid);
      build(a[root].rc, mid + 1, r);
  }
  void init(int x) {
      n = x; size = 0;
      build(root[0], 1, n);
  }
  int modify(int root, int l, int r, int pos) {
      int ans = ++size;
      a[ans] = a[root];
      a[ans].sum += 1;
      if (l == r) return ans;
      int mid = (l + r) / 2;
      if (mid >= pos) a[ans].lc = modify(a[root].lc, l, mid, pos);
      else a[ans].rc = modify(a[root].rc, mid + 1, r, pos);
      return ans;
  }
  void extend(int version, int pos) {
      root[version] = modify(root[version - 1], 1, n, pos);
  }
  int query(int rootl, int rootr, int l, int r, int ql, int qr) {
      if (l == ql && r == qr) return a[rootr].sum - a[rootl].sum;
      long long ans = 0;
      int mid = (l + r) / 2;
      if (mid >= ql) ans += query(a[rootl].lc, a[rootr].lc, l, mid, ql, min(mid, qr));
      if (mid + 1 <= qr) ans += query(a[rootl].rc, a[rootr].rc, mid + 1, r, max(mid + 1, ql), qr);
      return ans;
  }
  int query(int l, int r, int ql, int qr) {
      if (ql <= qr) return query(root[l - 1], root[r], 1, n, ql, qr);
      else return 0;
  }
} ST;
struct SuffixArray {
  int sa[MAXN], rnk[MAXN], height[MAXN];
  int Min[MAXN][MAXLOG], bit[MAXN], res[MAXN], N;
  int lcp(int x, int y) {
      if (x == y) return N - x + 1;
      x = rnk[x], y = rnk[y];
      if (x > y) swap(x, y);
      int tmp = bit[y - x];
      return min(Min[x][tmp], Min[y - (1 << tmp)][tmp]);
  }
  void init(char *a, int n) {
      N = n;
      static int x[MAXN], y[MAXN], cnt[MAXN], rk[MAXN];
      for (int i = 1; i <= MAXC; i++)
          cnt[i] = 0;
      for (int i = 1; i <= n; i++)
          cnt[(int) a[i]]++;
      for (int i = 1; i <= MAXC; i++)
          cnt[i] += cnt[i - 1];
      for (int i = n; i >= 1; i--)
          sa[cnt[(int) a[i]]--] = i;
      rnk[sa[1]] = 1;
      for (int i = 2; i <= n; i++)
          rnk[sa[i]] = rnk[sa[i - 1]] + (a[sa[i]] != a[sa[i - 1]]);
      for (int k = 1; rnk[sa[n]] != n; k <<= 1) {
          for (int i = 1; i <= n; i++) {
              x[i] = rnk[i];
              y[i] = (i + k <= n) ? rnk[i + k] : 0;
          }
          for (int i = 1; i <= n; i++)
              cnt[i] = 0;
          for (int i = 1; i <= n; i++)
              cnt[y[i]]++;
          for (int i = 1; i <= n; i++)
              cnt[i] += cnt[i - 1];
          for (int i = n; i >= 1; i--)
              rk[cnt[y[i]]--] = i;
          for (int i = 1; i <= n; i++)
              cnt[i] = 0;
          for (int i = 1; i <= n; i++)
              cnt[x[i]]++;
          for (int i = 1; i <= n; i++)
              cnt[i] += cnt[i - 1];
          for (int i = n; i >= 1; i--)
              sa[cnt[x[rk[i]]]--] = rk[i];
          rnk[sa[1]] = 1;
          for (int i = 2; i <= n; i++)
              rnk[sa[i]] = rnk[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]);       
      }
      int now = 0; sa[n + 1] = rnk[n + 1] = 0;
      for (int i = 1; i <= n; i++) {
          if (now) now--;
          while (a[i + now] == a[sa[rnk[i] + 1] + now]) now++;
          height[rnk[i]] = now;
      }
      for (int i = 1; i <= n; i++)
          Min[i][0] = height[i];
      for (int p = 1; p < MAXLOG; p++) {
          int tmp = 1 << (p - 1);
          for (int i = 1, j = tmp + 1; j <= n; i++, j++)
              Min[i][p] = min(Min[i][p - 1], Min[i + tmp][p - 1]);
      }
      for (int i = 1; i <= n; i++) {
          bit[i] = bit[i - 1];
          if (i >= 1 << (bit[i] + 1)) bit[i]++;
      }
      static set <int> st; st.clear();
      for (int i = n; i >= 1; i--) {
          res[i] = 0;
          set <int> :: iterator tmp = st.insert(rnk[i]).first;
          if (tmp != st.begin()) {
              set <int> :: iterator tnp = tmp; tnp--;
              chkmax(res[i], lcp(i, sa[*tnp]));
          }
          tmp++;
          if (tmp != st.end()) chkmax(res[i], lcp(i, sa[*tmp]));
      }
  }
} SA;
struct SuffixAutomaton {
  struct Node {
      int child[26];
      int father, depth;
  } a[MAXP];
  vector <int> b[MAXP];
  char s[MAXN], t[MAXN];
  int tot, list[MAXN];
  int root, size, last, n;
  int dfn[MAXP], rit[MAXP], mark[MAXP];
  int newnode(int depth) {
      a[size].depth = depth;
      a[size].father = 0l;
      memset(a[size].child, 0, sizeof(a[size].child));
      return size++;
  }
  void extend(int ch, int home) {
      int p = last, np = newnode(a[p].depth + 1);
      while (a[p].child[ch] == 0) {
          a[p].child[ch] = np;
          p = a[p].father;
      }
      if (a[p].child[ch] == np) a[np].father = root;
      else {
          int q = a[p].child[ch];
          if (a[p].depth + 1 == a[q].depth) a[np].father = q;
          else {
              int nq = newnode(a[p].depth + 1);
              memcpy(a[nq].child, a[q].child, sizeof(a[q].child));
              a[nq].father = a[q].father;
              a[np].father = a[q].father = nq;
              while (a[p].child[ch] == q) {
                  a[p].child[ch] = nq;
                  p = a[p].father;
              }
          }
      }
      last = np, mark[np] = home;
  }
  void dfs(int pos) {
      if (mark[pos]) dfn[pos] = ++tot, list[tot] = mark[pos];
      else dfn[pos] = tot + 1;
      for (unsigned i = 0; i < b[pos].size(); i++)
          dfs(b[pos][i]);
      rit[pos] = tot;
  }
  void init() {
      scanf("\n%s", s + 1);
      n = strlen(s + 1), size = 0;
      root = last = newnode(0);
      for (int i = n; i >= 1; i--)
          extend(s[i] - 'a', i);
      for (int i = 1; i < size; i++)
          b[a[i].father].push_back(i);
      tot = 0, dfs(0);
      ST.init(n);
      for (int i = 1; i <= n; i++)
          ST.extend(i, list[i]);
  }
  void work() {
      int q; read(q);
      while (q--) {
          int ql, qr;
          scanf("\n%s%d%d", t + 1, &ql, &qr);
          int qlen = strlen(t + 1);
          SA.init(t, qlen);
          int len = 0, now = root;
          long long ans = 0;
          for (int i = qlen; i >= 1; i--) {
              int ch = t[i] - 'a';
              while (now != root && a[now].child[ch] == 0) {
                  now = a[now].father;
                  chkmin(len, a[now].depth);
              }
              if (a[now].child[ch]) {
                  now = a[now].child[ch];
                  len++;
              }
              while (len != 0 && ST.query(dfn[now], rit[now], ql, qr - len + 1) == 0) {
                  len--;
                  if (a[a[now].father].depth == len)
                      now = a[now].father;
              }
              ans += qlen - i + 1 - max(len, SA.res[i]);
          }
          writeln(ans);
      }
  }
} SAM;
int main() {
  SAM.init();
  SAM.work();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值