Luogu P5112
大家好我后缀数组学魔怔了,所以我来写一发用到后缀数组的题解。
首先求出字符串 \(s\) 的 \(sa\),\(rk\) 和 \(ht\) 三个数组,用 \(i\) 代表 \(s\) 以 \(i\) 开头的后缀。
根据 \(\operatorname{lcp}(i, j) = \min\limits_{p=rk_i + 1}^{rk_j} ht_p\),问题转化为区间内有多少个点对满足 \(\min\limits_{p=rk_i + 1}^{rk_j} ht_p\ge k\),换句话说就是在 \((rk_i,rk_j]\) 这个区间里每个数都 \(\ge k\)。
由于每个数都 \(\ge k\),所以 \(<k\) 的 \(ht\) 就将原 \(ht\) 数组分成了若干块,\(rk\) 值在同一块里的点对有贡献,否则没贡献。设 \(c_i\) 为第 \(i\) 个块中的元素个数,问题转化为对于每个询问求 \(\sum \dfrac{c_i\times(c_i - 1)}2\)。
发现这个东西可以莫队,时间复杂度 \(O(n\log n + n\sqrt m)\),可过。
代码:
constexpr int N = 3e6 + 5;
int n, m, k, cnt[N], sa[N], id[N], __rk[2][N], *rk = __rk[0], *ork = __rk[1], ht[N];
char s[N];
void get_sa() {
int m = 128, p = 0;
rep(i, 1, n) ++cnt[rk[i] = s[i]];
rep(i, 1, m) cnt[i] += cnt[i - 1];
per(i, n, 1) sa[cnt[rk[i]]--] = i;
for(int w = 1; ; m = p, p = 0, w <<= 1) {
rep(i, n - w + 1, n) id[++p] = i;
rep(i, 1, n) if(sa[i] > w) id[++p] = sa[i] - w;
memset(cnt, 0, 4 * (m + 1));
rep(i, 1, n) ++cnt[rk[i]];
rep(i, 1, m) cnt[i] += cnt[i - 1];
per(i, n, 1) sa[cnt[rk[id[i]]]--] = id[i];
swap(rk, ork);
p = 0;
rep(i, 1, n) rk[sa[i]] = ork[sa[i]] == ork[sa[i - 1]] && ork[sa[i] + w] == ork[sa[i - 1] + w] ? p : ++p;
if(p == n) break;
}
}
void get_ht() {
int k = 0;
rep(i, 1, n) {
if(k) --k;
while(s[i + k] == s[sa[rk[i] - 1] + k]) ++k;
ht[rk[i]] = k;
}
}
int B, b[N], tot, bel[N], cc[N];
ll ans[N], res;
void get_b() {
rep(i, 1, n) if(ht[i] < k) b[++tot] = i;
b[tot + 1] = n + 1;
rep(i, 1, tot) rep(j, b[i], b[i + 1] - 1) bel[sa[j]] = i;
}
struct query {
int l, r, id;
bool operator<(const query& b) const {
return l / B != b.l / B ? l / B < b.l / B : (l / B & 1) ? r < b.r : r > b.r;
}
} q[N];
inline void add(int i) { res += cc[i]++; }
inline void del(int i) { res -= --cc[i]; }
void solve() {
int l = 1, r = 0;
rep(i, 1, m) {
while(l > q[i].l) add(bel[--l]);
while(r < q[i].r) add(bel[++r]);
while(l < q[i].l) del(bel[l++]);
while(r > q[i].r) del(bel[r--]);
// rep(i, 1, tot) cout << cc[i] << ' '; cout << endl;
ans[q[i].id] = res;
}
}
signed main() {
cin >> n >> m >> k;
cin >> (s + 1);
get_sa();
get_ht();
get_b();
// rep(i, 1, n) cout << sa[i] << ' '; cout << endl;
// rep(i, 1, n) cout << rk[i] << ' '; cout << endl;
// rep(i, 1, n) cout << ht[i] << ' '; cout << endl;
// rep(i, 1, tot) cout << b[i] << ' '; cout << endl;
// rep(i, 1, n) cout << bel[i] << ' '; cout << endl;
rep(i, 1, m) cin >> q[i].l >> q[i].r, q[i].id = i;
B = n / sqrt(m);
sort(q + 1, q + m + 1);
solve();
rep(i, 1, m) cout << ans[i] << endl;
return 0;
}

浙公网安备 33010602011771号