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; }