HDU-4622 Reincarnation

题意:给定一个字符串,有Q次询问,每次询问得出区间[L, R]内有多少个不同的子串。

分析:后缀数组搞,不过hash+dp也能够搞定这题,详解见http://www.cnblogs.com/Lyush/p/3233573.html

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
using namespace std;

const int N = 2005;
char str[N];
int len, seq[N];
int sa[N], rank[N], height[N];
int wa[N], wb[N], ws[N], wv[N];
int f[N][30];
int lg[N];

bool cmp(int r[], int a, int b, int l) {
    return r[a] == r[b] && r[a+l] == r[b+l];
}

void da(int r[], int sa[], int n, int m) {
    int i, j, p, *x = wa, *y = wb;
    for (i = 0; i < m; ++i) ws[i] = 0;
    for (i = 0; i < n; ++i) ws[x[i]=r[i]]++;
    for (i = 1; i < m; ++i) ws[i] += ws[i-1];
    for (i = n-1; i >= 0; --i) sa[--ws[x[i]]] = i;
    for (j = 1, p = 1; p < n; j *= 2, m = p) {
        for (p = 0, i = n - j; i < n; ++i) y[p++] = i;
        for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
        for (i = 0; i < n; ++i) wv[i] = x[y[i]];
        for (i = 0; i < m; ++i) ws[i] = 0;
        for (i = 0; i < n; ++i) ws[wv[i]]++;
        for (i = 1; i < m; ++i) ws[i] += ws[i-1];
        for (i = n-1; i >= 0; --i) sa[--ws[wv[i]]] = y[i];
        for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
    }
}

void calheight(int r[], int sa[], int n) {
    int i, j, k = 0;
    for (i = 1; i <= n; ++i) rank[sa[i]] = i;
    for (i = 0; i < n; height[rank[i++]] = k)
        for (k?k--:0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; ++k) ;
}

void initrmq() {
    int LIM = (int)log2(1.0*N);
    for (int i = 1; i <= len; ++i) {
        f[i][0] = height[i];
    }
    for (int j = 1; j <= LIM; ++j) {
        for (int i = 1; i+(1<<j)-1 <= len; ++i) {
            f[i][j] = min(f[i][j-1], f[i+(1<<j-1)][j-1]);
        }
    }
}

int query(int l, int r) {
    int k = lg[r-l+1];
    return min(f[l][k], f[r-(1<<k)+1][k]);
}

int cal(int l, int r) {
    int ret = (r-l+2)*(r-l+1)/2, last = -1;
    int lcp, a, b, alen, blen, k = r-l+1;
    for (int i = 1; i <= len && k; ++i) {
        if (sa[i] >= l && sa[i] <= r) { // 说明该后缀位于所选的区间内,且全局rank最靠前
            k--;
            if (last != -1) {
                a = last, b = i;
                if (a > b) swap(a, b);
                lcp = query(a+1, b);
                alen = r-sa[last]+1, blen = r-sa[i]+1;
            /*    if (lcp < alen && lcp < blen) {last = i;}
                else if (lcp >= alen && lcp >= blen) {
                    if (blen > alen) last = i;
                }
                else if (lcp >= alen) {last = i;}
            */
                if (alen > blen && lcp >= blen) {}
                else last = i;
                ret -= min(lcp, min(alen, blen));
            } else last = i;
        }
    }
    return ret;
}

void solve() {
    int Q, l, r;
    scanf("%d", &Q);
    while (Q--) {
        scanf("%d %d", &l, &r);
        printf("%d\n", cal(l-1, r-1)); // 字符串编号从0开始
    }
}

int main() {
    lg[0] = -1;
    for (int i = 1; i < N; ++i) {
        lg[i] = lg[i>>1] + 1;
    }
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%s", str);
        len = strlen(str);
        for (int i = 0; i < len; ++i) seq[i] = str[i]-'a'+1;
        seq[len] = 0;
        da(seq, sa, len+1, 27);
        calheight(seq, sa, len);
        initrmq();
        solve();
    }
    return 0;
}
View Code

相关文章:

  • 2021-08-08
  • 2021-11-30
  • 2022-02-11
  • 2021-06-27
  • 2022-01-12
  • 2022-02-25
  • 2021-11-23
  • 2021-10-25
猜你喜欢
  • 2021-07-07
  • 2021-10-03
  • 2022-03-03
  • 2021-08-07
  • 2021-11-26
  • 2021-06-07
  • 2021-07-24
相关资源
相似解决方案