uoj395
题意
给串 \(s\)。
\(q\) 次询问,每次给串 \(t\) 和 \(l,\ r\) 问有多少个不同字符串 \(r\) 满足 \(r\) 为 \(t\) 的子串且 \(r\) 不为 \(s[l...r]\) 的子串。
\(1\ \leq\ |s|\ \leq\ 5\ *\ 10^5,\ 1\ \leq\ q\ \leq\ 10^5,\ 1\ \leq\ \sum\ |T|\ \leq\ 10^6\)
做法1
每次询问对于 \(i\) 只要求出最大的 \(len\) 使得 \(t[i...i\ +\ len_i\ -\ 1]\) 在 \(s[l...r]\) 中出现或 \(t[i\ +\ 1...]\) 中出现即可。可以发现 \(len_i\ \leq\ len_{i\ +\ 1}\ +\ 1\),所以从后往前扫,每次暴力求 \(len\) 即可。用个 \(SA\) 维护,时间复杂度 \(O(n\ log\ n)\)。
注意 \(ST\) 表询问时预处理 \(2\) 的次幂常数较小。
uoj 评测鸡好不稳定啊,这份代码大概会在 76 到 96 分之间随机。qaq
代码
#include <bits/stdc++.h>
#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif
using namespace std;
struct fast_io {
static const int istream_size = (1 << 22) | 10, ostream_size = (1 << 21) | 10;
char istream[istream_size], ostream[ostream_size], *i, *o;
fast_io() { fread(istream, 1, istream_size, stdin); i = istream; o = ostream; }
~fast_io() { fwrite(ostream, 1, o - ostream, stdout); }
void read(char *s, int &n) {
while(*i < 'a') ++i;
n = 0;
while(*i >= 'a') s[n++] = *i++;
return;
}
void read(int &x) {
while(*i < '0') ++i;
x = 0;
while(*i >= '0') x = x * 10 + *i++ - '0';
return;
}
void print(long long &x) {
if(!x) { *o++ = '0'; *o++ = '\n'; return; }
static char s[20], *i;
i = s;
while(x) {
long long y = x / 10;
*i++ = x - y * 10 + '0';
x = y;
}
while(i != s) *o++ = *--i;
*o++ = '\n';
return;
}
} io;
struct query {
int beg, End, l, r, id;
query() {}
query(int beg, int End, int l, int r, int id): beg(beg), End(End), l(l), r(r), id(id) {}
};
struct segment_tree {
static const int oo = 1e9;
int n, N;
vector<int> num;
segment_tree() {}
segment_tree(int _n) { // [1, _n]
n = _n;
N = 1; while(N - 1 <= _n) N <<= 1;
num = vector<int>(N << 1, oo);
}
inline void M(int i, const int &x) { for (i |= N; i; i >>= 1) num[i] = min(num[i], x); return; }
inline int Q(int s, int t) {
int ret = oo;
for (s = (s | N) - 1, t = (t | N) + 1; s ^ t ^ 1; s >>= 1, t >>= 1) {
if(~s & 1) ret = min(ret, num[s ^ 1]);
if(t & 1) ret = min(ret, num[t ^ 1]);
}
return ret;
}
};
const int maxn = 2e6 + 10;
char s[maxn]; int s_size;
int main() {
io.read(s, s_size);
int End = s_size;
int q; io.read(q);
vector<query> qry(q); vector<long long> ans(q, 0);
for (int i = 0; i < q; ++i) {
s[s_size++] = '#'; int beg = s_size;
int t_size; io.read(s + s_size, t_size); s_size += t_size;
int l, r; io.read(l); io.read(r);
qry[i] = query(beg, s_size - 1, l - 1, r - 1, i);
}
sort(qry.begin(), qry.end(), [&](query a, query b) { return a.l > b.l; });
int n = s_size;
segment_tree seg(n);
vector<int> lim(n + 1, 0);
vector<int> sa(n + 1), rnk(n + 1), LOG(n + 1, 0);
for (int i = 2; i <= n; ++i) LOG[i] = LOG[i >> 1] + 1;
vector<vector<int> > h(LOG[n] + 1, vector<int>(n + 1, 0));
vector<int> pw2(LOG[n] + 1);
for (int i = 0; i < pw2.size(); ++i) pw2[i] = 1 << i;
function<void(vector<int> &)> pre_sum = [&](vector<int> &v) {
for (int i = 1; i < v.size(); ++i) v[i] += v[i - 1];
return;
};
function<void()> build_SA = [&]() {
vector<int> cnt(max(n + 3, 300), 0);
vector<pair<pair<int, int>, int> > p(n), q(n);
for (int i = 0; i < n; ++i) cnt[s[i]] = 1;
pre_sum(cnt);
int mx = cnt.back();
for (int i = 0; i < n; ++i) rnk[i] = cnt[s[i]];
for (int len = 1; mx < n; len <<= 1) {
fill(cnt.begin(), cnt.end(), 0);
for (int i = 0; i < n; ++i) {
p[i] = make_pair(make_pair(rnk[i], (i + len < n ? rnk[i + len] : 0)), i);
++cnt[p[i].first.second + 1];
}
pre_sum(cnt);
for (int i = 0; i < n; ++i) q[cnt[p[i].first.second]++] = p[i];
fill(cnt.begin(), cnt.end(), 0);
for (int i = 0; i < n; ++i) ++cnt[q[i].first.first + 1];
pre_sum(cnt);
for (int i = 0; i < n; ++i) p[cnt[q[i].first.first]++] = q[i];
mx = 0;
for (int i = 0; i < n; ++i) {
if(!i || p[i].first != p[i - 1].first) ++mx;
rnk[p[i].second] = mx;
}
}
rnk[n] = 0;
for (int i = 0; i <= n; ++i) sa[rnk[i]] = i;
for (int k = 0, i = 0; i < n; ++i) {
if(k) --k;
int j = sa[rnk[i] - 1];
while(i + k < n && j + k < n && s[i + k] == s[j + k]) ++k;
h[0][rnk[i]] = k;
}
for (int lg = 1; lg < h.size(); ++lg) {
int len = 1 << lg - 1;
for (int i = 1; i + len <= n; ++i) h[lg][i] = min(h[lg - 1][i], h[lg - 1][i + len]);
}
return;
};
auto hQ = [&](const int &l, const int &r) {
int lg = LOG[r - l + 1];
return min(h[lg][l], h[lg][r - pw2[lg] + 1]);
};
auto lcp = [&](int i, int j) {
i = rnk[i]; j = rnk[j];
return hQ(min(i, j) + 1, max(i, j));
};
auto find_lb = [&](const int &rnk_i, const int &len, const int &lst) {
int lb = 1, rb = lst - 1;
while(lb <= rb) {
int mid = lb + rb >> 1;
if(hQ(mid + 1, rnk_i) >= len) rb = mid - 1;
else lb = mid + 1;
}
return lb;
};
auto find_rb = [&](const int &rnk_i, const int &len, const int &lst) {
int lb = lst + 1, rb = n;
while(lb <= rb) {
int mid = lb + rb >> 1;
if(hQ(rnk_i + 1, mid) >= len) lb = mid + 1;
else rb = mid - 1;
}
return rb;
};
build_SA();
for (int i = End - 1, j = 0; ~i && j < q; --i) {
seg.M(rnk[i], i);
while(j < q && qry[j].l == i) {
vector<int> pos;
for (int k = qry[j].beg; k <= qry[j].End; ++k) pos.push_back(k);
sort(pos.begin(), pos.end(), [&](int i, int j) { return rnk[i] < rnk[j]; });
vector<int> stk;
for (int i: pos) {
while(stk.size() && stk.back() < i) stk.pop_back();
if(stk.size()) lim[i] = max(lim[i], lcp(i, stk.back()));
stk.push_back(i);
}
reverse(pos.begin(), pos.end()); stk.clear();
for (int i: pos) {
while(stk.size() && stk.back() < i) stk.pop_back();
if(stk.size()) lim[i] = max(lim[i], lcp(i, stk.back()));
stk.push_back(i);
}
int len = 0;
for (int k = qry[j].End; k >= qry[j].beg; --k) {
++len;
int lst_lb, lst_rb; lst_lb = lst_rb = rnk[k];
while(len > 0) {
int lb = find_lb(rnk[k], len, lst_lb), rb = find_rb(rnk[k], len, lst_rb);
if(seg.Q(lst_lb = lb, lst_rb = rb) + len - 1 <= qry[j].r) break;
--len;
}
ans[qry[j].id] += (qry[j].End - k + 1 - max(len, lim[k]));
}
++j;
}
}
for (auto t: ans) io.print(t);
return 0;
}