zoukankan      html  css  js  c++  java
  • 后缀数组小记

    求 sa, rnk

    定义

    (sa[i]) 表示第 (i) 小的后缀对应原串的位置
    (rk[i]) 表示第 (i) 个后缀的排名
    (x[i]) 表示第 (i) 个后缀的第一关键字排名,即当前的 (rk[i])
    (y[i]) 表示第 (i) 小的第二关键字对应的第几个后缀
    (c[i]) 是一个计数数组,用于基数排序用

    方法

    考虑倍增,每次从 (2^k) 转移到 (2^{k+1}) ,可以发现每个 (2^{k+1}) 串可以表示为两个 (2^k) 的子串,分别对应两个关键字。
    用基数排序即可。
    复杂度 (O(nlogn))

    求 LCP

    定义 (LCP(i,j)) 表示 (suf(sa[i]))(suf(sa[j]))最长公共前缀长度。

    引理

    显然有:

    • (LCP(i,j) = LCP(j,i))
    • (LCP(i,i) = n - sa[i] + 1)

    LCP Lemma

    考虑一个强大的结论:
    对于任意 (1le i< k< jle n),均有 (LCP(i,j)=min(LCP(i,k),LCP(k,j)))
    证明:
    (p=min(LCP(i,k),LCP(k,j))) ,则有 (LCP(i,k)ge p, LCP(k,j)ge p)
    (suf(sa[i]) = u, suf(sa[k]) = v, suf(sa[j]) = w)
    则由 (LCP) 定义知 ((u)(v))、((v)(w)) 前 (p) 个字符均相等。
    因此 (u)(w) 的前 (p) 个字符相等,所以 (LCP(i,j)ge p)
    假设 (LCP(i,j)=q > p) ,那么 (qge p + 1) ,即 (u[p+1]=w[p+1])
    因为 (p=min(LCP(i,k),LCP(k,j))) ,所以 (u[p+1]≠v[p+1])(v[p+1]≠w[p+1])
    因为 (suf(sa[i])<suf(sa[j])<suf(a[k])) ,所以 (u[p+1]=v[p+1]=w[p+1])
    矛盾,故 (LCP(i,j)le p)
    因此 (LCP(i,j)=p=min(LCP(i,k),LCP(k,j)))

    LCP Theorem

    终极结论:
    (LCP(i,j)=min(LCP(k-1,k) | i < kle j))
    证明:
    (LCP Lemma)(LCP(i,j)=min(LCP(i,j-1),LCP(j-1,j))=min(min(LCP(i,j-2),LCP(j-2,j-1)),LCP(j-1,j))=...=min(LCP(k-1,k) | i < kle j))

    推导

    前面都是铺垫。
    我们设 (height[i]=LCP(i-1,i)) ((1<ile n)) ,显然 (height[1] = 0)
    (LCP Theorem)(LCP(i,j)=min(height[k] | i<kle j))
    那么,这个 (height) 该咋求呢?
    继续定义 (h[i] = height[rk[i]]) ,则有 (height[i] = h[sa[i]])
    下面来证明一个最关键的定理:(h[i]ge h[i-1]-1) ,即 (height[rk[i]]ge height[rk[i-1]] - 1)
    也即 (LCP(rk[i]-1,rk[i])ge LCP(rk[i-1]-1,rk[i-1])-1)
    证明:
    不妨设第 (i-1) 个字符串按排名来前面的是第 (k) 个字符串。
    (height) 定义知第 (k) 个字符串和第 (i-1) 个字符串的 (LCP)(height[rk[i-1]])
    下面我们来讨论两字符串间的关系:

    • (k) 个字符串和第 (i-1) 个字符串的首字符不同

    那么第 (k+1) 个字符串的排名既有可能在 (i) 前面,也有可能在 (i) 后面。
    但没关系,此时 (height[rk[i-1]]=0) ,一定满足 (height[rk[i]] ge height[rk[i-1]] - 1)

    • (k) 个字符串和第 (i-1) 个字符串的首字符相同

    由于第 (k+1) 个字符串就是第 (k) 个字符串去掉首字符得到的,第 (i) 个字符串就是第 (i-1) 个字符串去掉首字符得到的,所以第 (k+1) 个字符串和第 (i) 个字符串的 (LCP) 就是 (height[rk[i-1]]-1)
    注意第 (k+1) 个字符不一定是第 (sa[rk[i]-1]) 个字符串,但是因为第 (sa[rk[i]-1]) 个字符串和第 (i) 个字符串的 (LCP) 最大,即 (height[rk[i]]=LCP(rk[i]-1,rk[i])ge LCP(rk[k+1],rk[i])=height[rk[i-1]]-1)

    综上所述,可证得 (h[i]ge h[i-1]-1)

    贴份代码,跑路

    // Author: wlzhouzhuan
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    #define ull unsigned long long
    #define rint register int
    #define rep(i, l, r) for (rint i = l; i <= r; i++)
    #define per(i, l, r) for (rint i = l; i >= r; i--)
    #define mset(s, _) memset(s, _, sizeof(s))
    #define pb push_back
    #define pii pair <int, int>
    #define mp(a, b) make_pair(a, b)
    #define debug(x) cerr << #x << " = " << x << '
    ';
    #define pll pair <ll, ll>
    #define fir first
    #define sec second
    
    inline int read() {
      int x = 0, neg = 1; char op = getchar();
      while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
      while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
      return neg * x;
    }
    inline void print(int x) {
      if (x < 0) { putchar('-'); x = -x; }
      if (x >= 10) print(x / 10);
      putchar(x % 10 + '0');
    }
    
    const int N = 1000005;
    
    char s[N];
    int n, m;
    
    int sa[N], rk[N], x[N], y[N];
    int cnt[N];
    void SA() {
      for (rint i = 1; i <= m; i++) cnt[i] = 0;
      for (rint i = 1; i <= n; i++) cnt[x[i] = s[i]]++;
      for (rint i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
      for (rint i = n; i >= 1; i--) sa[cnt[x[i]]--] = i;
      int p;
      for (rint k = 1; k <= n; k <<= 1) {
        int cur = 0;
        for (rint i = n - k + 1; i <= n; i++) y[++cur] = i;
        for (rint i = 1; i <= n; i++) if (sa[i] > k) y[++cur] = sa[i] - k;
        
        for (rint i = 1; i <= m; i++) cnt[i] = 0;
        for (rint i = 1; i <= n; i++) cnt[x[i]]++;
        for (rint i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
        for (rint i = n; i >= 1; i--) sa[cnt[x[y[i]]]--] = y[i];
        
        swap(x, y);
        x[sa[1]] = 1, p = 1;
        for (rint i = 2; i <= n; i++) {
          x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p : ++p);
        }
        if (p == n) break;
        m = p;
      }
      memcpy(rk, x, sizeof(x));
    }
    
    int height[N], h[N][20], lg[N];
    int getheight() {
      int k = 0;
      for (rint i = 1; i <= n; i++) {
        if (rk[i] == 1) continue;
        if (k) --k;
        int j = sa[rk[i] - 1];
        while (j + k <= n && i + k <= n && s[i + k] == s[j + k]) k++;
        height[rk[i]] = k;
        h[rk[i]][0] = k; 
      }
      for (rint j = 1; j < 20; j++) {
        for (rint i = 1; i + (1 << j) - 1 <= n; i++) {
          h[i][j] = min(h[i][j - 1], h[i + (1 << j - 1)][j - 1]);
        }
      }
    }
    int LCP(int l, int r) {
      if (l > r) swap(l, r);
      if (l == r) return n - sa[l] + 1;
      l++;
      int LEN = lg[r - l];
      return min(h[l][LEN], h[r - (1 << LEN) + 1][LEN]);
    }
    
    int main() {
      scanf("%s", s + 1);
      n = strlen(s + 1);
      m = 150;
      SA();
      getheight();
      for (rint i = 1; i <= n; i++) {
        printf("%d ", sa[i]);
      }
      puts("");
      
      lg[1] = 0;
      for (rint i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
      int l, r;
      while (~scanf("%d%d", &l, &r)) {
        printf("%d
    ", LCP(rk[l], rk[r]));
      }
      return 0;
    }
    
  • 相关阅读:
    Android安全机制
    service不死之身
    图片加载机制比较
    handler机制面试
    SharedPreferences封装类
    文字太长自动缩小
    ANR
    onCreate源码分析
    线程池的启动策略
    Oauth认证协议
  • 原文地址:https://www.cnblogs.com/wlzhouzhuan/p/13244529.html
Copyright © 2011-2022 走看看