Description
人的一生不仅要靠自我奋斗,还要考虑到历史的行程。
历史的行程可以抽象成一个01串,作为一个年纪比较大的人,你希望从历史的行程中获得一些知识。
你发现,一件事情可以看成是这个01串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。
两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。
现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?
Input
第一行两个正整数n,m,表示串长和询问个数。
第二行长度为n的01串,表示历史的行程。
接下来m行,每行两个正整数l,r表示询问的区间是第l个位置到第r个位置,包含端点,保证1<=l<=r<=n。
Output
输出m行,对每个询问输出一个整数表示最大的相似度。
Sample Input
4 2
0101
1 3
2 4
Sample Output
1
2
Data Constraint
思路
考虑建出 SA 之后按 height 从小到大合并,维护每个块中有哪些询问。
bitset 大法好,O(n ^ 2 / w)
代码
#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#define mid (l + r >> 1)
#define ls (k << 1)
#define rs (k << 1 | 1)
using namespace std;
typedef set<int>::iterator I;
const int maxn = 100010;
int n, m, SZ, ans[maxn], rk[maxn], sa[maxn], mx[maxn << 2];
int ht[maxn], buc[maxn], sc[maxn], fa[maxn], id[maxn], cur[maxn];
char s[maxn];
vector<pair<int, int> > Q[maxn], M[maxn];
set<int> pos[maxn];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
bool comp(int i, int j) { return ht[i] > ht[j]; }
void radix_sort() {
for (int i = 1; i <= SZ; i++) buc[i] = 0;
for (int i = 1; i <= n; i++) buc[rk[i]]++;
for (int i = 1; i <= SZ; i++) buc[i] += buc[i - 1];
for (int i = n; i; i--) sa[buc[rk[sc[i]]]--] = sc[i];
}
void calc_sa() {
for (int i = 1; i <= n; i++) {
rk[i] = s[i] - '0' + 1, sc[i] = i;
}
SZ = 2, radix_sort();
for (int k = 1; k <= n; k <<= 1) {
int cnt = 0;
for (int i = n - k + 1; i <= n; i++) sc[++cnt] = i;
for (int i = 1; i <= n; i++) {
if (sa[i] > k) sc[++cnt] = sa[i] - k;
}
radix_sort(), swap(rk, sc), rk[sa[1]] = cnt = 1;
for (int i = 2; i <= n; i++) {
rk[sa[i]] = (sc[sa[i]] == sc[sa[i - 1]] &&
sc[sa[i] + k] == sc[sa[i - 1] + k] ? cnt : ++cnt);
}
SZ = cnt; if (cnt == n) break;
}
for (int i = 1, j = 0; i <= n; i++) {
if (rk[i] == 1) { j = ht[rk[i]] = 0; continue; }
j = max(0, j - 1);
while (s[sa[rk[i] - 1] + j] == s[i + j]) j++;
ht[rk[i]] = j;
}
}
void init() {
for (int i = 1; i <= n; i++) {
fa[i] = id[i] = i, pos[i].insert(i);
}
sort(id + 2, id + n + 1, comp);
for (int i = 2; i <= n; i++) {
int x = sa[id[i]], y = sa[id[i] - 1], len = ht[id[i]];
int fx = find(x), fy = find(y);
if (pos[fx].size() > pos[fy].size()) swap(fx, fy);
for (I it1 = pos[fy].begin(); it1 != pos[fy].end(); it1++) {
I it2 = pos[fx].insert(*it1).first, it3;
if (it2 != pos[fx].begin()) {
it3 = it2; it3--;
if (find(*it2) != find(*it3)) {
M[*it3].push_back(make_pair(*it2, len));
}
}
it3 = it2; it3++;
if (it3 != pos[fx].end() && find(*it2) != find(*it3)) {
M[*it2].push_back(make_pair(*it3, len));
}
}
fa[fy] = fx;
}
}
void upd(int k, int l, int r, int p, int v) {
if (l == r) { mx[k] = v; return; }
if (mid >= p) upd(ls, l, mid, p, v);
else upd(rs, mid + 1, r, p, v);
mx[k] = max(mx[ls], mx[rs]);
}
int query(int k, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) return mx[k];
int ans = 0;
if (mid >= ql) ans = max(ans, query(ls, l, mid, ql, qr));
if (mid < qr) ans = max(ans, query(rs, mid + 1, r, ql, qr));
return ans;
}
int main()
{
freopen("history.in", "r", stdin);
freopen("history.out", "w", stdout);
scanf("%d %d %s", &n, &m, s + 1);
reverse(s + 1, s + n + 1), calc_sa(), init();
for (int i = 1, l, r; i <= m; i++)
{
scanf("%d %d", &r, &l);
l = n - l + 1, r = n - r + 1;
Q[l].push_back(make_pair(r, i));
}
for (int i = n; i; i--)
{
for (int j = 0; j < M[i].size(); j++)
{
cur[M[i][j].first] = max(cur[M[i][j].first], M[i][j].second);
upd(1, 1, n, M[i][j].first, cur[M[i][j].first]);
}
for (int j = 0; j < Q[i].size(); j++)
{
ans[Q[i][j].second] = query(1, 1, n, i, Q[i][j].first);
}
}
for (int i = 1; i <= m; i++)
{
printf("%d\n", ans[i]);
}
return 0;
}