This documentation is automatically generated by online-judge-tools/verification-helper
#include "string/suffix-array.hpp"
#pragma once
#include "../template/template.hpp"
template <class Str>
struct SuffixArray {
// data
Str str;
vector<int> sa; // sa[i] : the starting index of the i-th smallest suffix
// (i = 0, 1, ..., n)
vector<int> rank; // rank[sa[i]] = i
vector<int>
lcp; // lcp[i]: the lcp of sa[i] and sa[i+1] (i = 0, 1, ..., n-1)
SparseTable<int> st; // use for calcultating lcp(i, j)
// getter
int &operator[](int i) { return sa[i]; }
const int &operator[](int i) const { return sa[i]; }
vector<int> get_sa() { return sa; }
vector<int> get_rank() { return rank; }
vector<int> get_lcp() { return lcp; }
// constructor
SuffixArray() {}
SuffixArray(const Str &str_, bool no_limit_elements = false) : str(str_) {
build_sa(no_limit_elements);
}
void init(const Str &str_, bool no_limit_elements = false) {
str = str_;
build_sa(no_limit_elements);
}
void build_sa(bool no_limit_elements = false) {
vector<int> s;
int num_of_chars = 256;
if (!no_limit_elements) {
for (int i = 0; i < (int)str.size(); ++i) {
s.push_back(str[i] + 1);
}
} else {
unordered_map<int, int> dict;
for (int i = 0; i < (int)str.size(); ++i) {
if (!dict.count(str[i])) dict[str[i]] = dict.size();
}
for (int i = 0; i < (int)str.size(); ++i) {
s.push_back(dict[str[i]] + 1);
}
num_of_chars = (int)dict.size();
}
s.push_back(0);
sa = sa_is(s, num_of_chars);
build_lcp(s);
build_sparse_table();
}
// SA-IS
// num_of_chars: # of characters
vector<int> sa_is(vector<int> &s, int num_of_chars) {
int N = (int)s.size();
if (N == 0)
return {};
else if (N == 1)
return {0};
else if (N == 2) {
if (s[0] < s[1])
return {0, 1};
else
return {1, 0};
}
vector<int> isa(N);
vector<bool> ls(N, false);
for (int i = N - 2; i >= 0; --i) {
ls[i] = (s[i] == s[i + 1]) ? ls[i + 1] : (s[i] < s[i + 1]);
}
vector<int> sum_l(num_of_chars + 1, 0), sum_s(num_of_chars + 1, 0);
for (int i = 0; i < N; ++i) {
if (!ls[i])
++sum_s[s[i]];
else
++sum_l[s[i] + 1];
}
for (int i = 0; i <= num_of_chars; ++i) {
sum_s[i] += sum_l[i];
if (i < num_of_chars) sum_l[i + 1] += sum_s[i];
}
auto induce = [&](const vector<int> &lms) -> void {
fill(isa.begin(), isa.end(), -1);
vector<int> buf(num_of_chars + 1);
copy(sum_s.begin(), sum_s.end(), buf.begin());
for (auto d : lms) {
if (d == N) continue;
isa[buf[s[d]]++] = d;
}
copy(sum_l.begin(), sum_l.end(), buf.begin());
isa[buf[s[N - 1]]++] = N - 1;
for (int i = 0; i < N; ++i) {
int v = isa[i];
if (v >= 1 && !ls[v - 1]) {
isa[buf[s[v - 1]]++] = v - 1;
}
}
copy(sum_l.begin(), sum_l.end(), buf.begin());
for (int i = N - 1; i >= 0; --i) {
int v = isa[i];
if (v >= 1 && ls[v - 1]) {
isa[--buf[s[v - 1] + 1]] = v - 1;
}
}
};
vector<int> lms, lms_map(N + 1, -1);
int M = 0;
for (int i = 1; i < N; ++i) {
if (!ls[i - 1] && ls[i]) {
lms_map[i] = M++;
}
}
lms.reserve(M);
for (int i = 1; i < N; ++i) {
if (!ls[i - 1] && ls[i]) {
lms.push_back(i);
}
}
induce(lms);
if (M) {
vector<int> lms2;
lms2.reserve(isa.size());
for (auto v : isa) {
if (lms_map[v] != -1) lms2.push_back(v);
}
int rec_upper = 0;
vector<int> rec_s(M);
rec_s[lms_map[lms2[0]]] = 0;
for (int i = 1; i < M; ++i) {
int l = lms2[i - 1], r = lms2[i];
int nl = (lms_map[l] + 1 < M) ? lms[lms_map[l] + 1] : N;
int nr = (lms_map[r] + 1 < M) ? lms[lms_map[r] + 1] : N;
bool same = true;
if (nl - l != nr - r)
same = false;
else {
while (l < nl) {
if (s[l] != s[r]) break;
++l, ++r;
}
if (l == N || s[l] != s[r]) same = false;
}
if (!same) ++rec_upper;
rec_s[lms_map[lms2[i]]] = rec_upper;
}
auto rec_sa = sa_is(rec_s, rec_upper);
vector<int> sorted_lms(M);
for (int i = 0; i < M; ++i) {
sorted_lms[i] = lms[rec_sa[i]];
}
induce(sorted_lms);
}
return isa;
}
// find min id that str.substr(sa[id]) >= T
int lower_bound(const Str &T) {
int left = -1, right = sa.size();
while (right - left > 1) {
int mid = (left + right) / 2;
if (str.compare(sa[mid], string::npos, T) < 0)
left = mid;
else
right = mid;
}
return right;
}
// find min id that str.substr(sa[id], T.size()) > T
int upper_bound(const Str &T) {
int left = -1, right = sa.size();
while (right - left > 1) {
int mid = (left + right) / 2;
if (str.compare(sa[mid], T.size(), T) <= 0)
left = mid;
else
right = mid;
}
return right;
}
// find min id that sa[id] >= str.substr(l, r-l)
int lower_bound(int l, int r) {
int left = -1, right = rank[l];
while (right - left > 1) {
int mid = (left + right) / 2;
if (st.get(mid, rank[l]) < r - l)
left = mid;
else
right = mid;
}
return right;
}
// search
bool is_contain(const Str &T) {
int lb = lower_bound(T);
if (lb >= sa.size()) return false;
return str.compare(sa[lb], T.size(), T) == 0;
}
// find lcp
void build_lcp(const vector<int> &s) {
int N = (int)s.size();
rank.assign(N, 0), lcp.assign(N - 1, 0);
for (int i = 0; i < N; ++i) rank[sa[i]] = i;
int h = 0;
for (int i = 0; i < N - 1; ++i) {
int pi = sa[rank[i] - 1];
if (h > 0) --h;
for (; pi + h < N && i + h < N; ++h) {
if (s[pi + h] != s[i + h]) break;
}
lcp[rank[i] - 1] = h;
}
}
// build sparse table for calculating lcp
void build_sparse_table() { st.init(lcp); }
// calc lcp of str.sutstr(a) and str.substr(b)
int get_lcp(int a, int b) {
return st.get(min(rank[a], rank[b]), max(rank[a], rank[b]));
}
// debug
void dump() {
for (int i = 0; i < sa.size(); ++i) {
cout << i << ": " << sa[i] << ", " << str.substr(sa[i]) << endl;
}
}
};
#line 2 "template/template.hpp"
#include <bits/stdc++.h>
#line 3 "template/macro.hpp"
#define overload3(_1, _2, _3, name, ...) name
#define all1(v) std::begin(v), std::end(v)
#define all2(v, a) std::begin(v), std::begin(v) + a
#define all3(v, a, b) std::begin(v) + a, std::begin(v) + b
#define all(...) overload3(__VA_ARGS__, all3, all2, all1)(__VA_ARGS__)
#define rall1(v) std::rbegin(v), std::rend(v)
#define rall2(v, a) std::rbegin(v), std::rbegin(v) + a
#define rall3(v, a, b) std::rbegin(v) + a, std::rbegin(v) + b
#define rall(...) overload3(__VA_ARGS__, rall3, rall2, rall1)(__VA_ARGS__)
#define elif else if
#define updiv(N, X) (((N) + (X) - 1) / (X))
#define sigma(a, b) (((a) + (b)) * ((b) - (a) + 1) / 2)
#define INT(...) \
int __VA_ARGS__; \
scan(__VA_ARGS__)
#define LL(...) \
ll __VA_ARGS__; \
scan(__VA_ARGS__)
#define STR(...) \
string __VA_ARGS__; \
scan(__VA_ARGS__)
#define CHR(...) \
char __VA_ARGS__; \
scan(__VA_ARGS__)
#define DOU(...) \
double __VA_ARGS__; \
scan(__VA_ARGS__)
#define LD(...) \
ld __VA_ARGS__; \
scan(__VA_ARGS__)
#define pb push_back
#define eb emplace_back
#line 3 "template/alias.hpp"
using ll = long long;
using ld = long double;
using pii = std::pair<int, int>;
using pll = std::pair<ll, ll>;
constexpr int inf = 1 << 30;
constexpr ll INF = 1LL << 60;
constexpr int dx[8] = {1, 0, -1, 0, 1, -1, 1, -1};
constexpr int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};
constexpr int mod = 998244353;
constexpr int MOD = 1e9 + 7;
#line 3 "template/func.hpp"
template <typename T>
inline bool chmax(T& a, T b) { return ((a < b) ? (a = b, true) : (false)); }
template <typename T>
inline bool chmin(T& a, T b) { return ((a > b) ? (a = b, true) : (false)); }
template <typename T, typename U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
os << p.first << " " << p.second;
return os;
}
template <typename T, typename U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
is >> p.first >> p.second;
return is;
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
for (auto it = std::begin(v); it != std::end(v);) {
os << *it << ((++it) != std::end(v) ? " " : "");
}
return os;
}
template <typename T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
for (T &in : v) {
is >> in;
}
return is;
}
inline void scan() {}
template <class Head, class... Tail>
inline void scan(Head &head, Tail &...tail) {
std::cin >> head;
scan(tail...);
}
template <class T>
inline void print(const T &t) { std::cout << t << '\n'; }
template <class Head, class... Tail>
inline void print(const Head &head, const Tail &...tail) {
std::cout << head << ' ';
print(tail...);
}
template <class... T>
inline void fin(const T &...a) {
print(a...);
exit(0);
}
#line 3 "template/util.hpp"
struct IOSetup {
IOSetup() {
std::cin.tie(nullptr);
std::ios::sync_with_stdio(false);
std::cout.tie(0);
std::cout << std::fixed << std::setprecision(12);
std::cerr << std::fixed << std::setprecision(12);
}
} IOSetup;
#line 3 "template/debug.hpp"
#ifdef LOCAL
#include <dump.hpp>
#else
#define debug(...)
#endif
#line 8 "template/template.hpp"
using namespace std;
#line 3 "string/suffix-array.hpp"
template <class Str>
struct SuffixArray {
// data
Str str;
vector<int> sa; // sa[i] : the starting index of the i-th smallest suffix
// (i = 0, 1, ..., n)
vector<int> rank; // rank[sa[i]] = i
vector<int>
lcp; // lcp[i]: the lcp of sa[i] and sa[i+1] (i = 0, 1, ..., n-1)
SparseTable<int> st; // use for calcultating lcp(i, j)
// getter
int &operator[](int i) { return sa[i]; }
const int &operator[](int i) const { return sa[i]; }
vector<int> get_sa() { return sa; }
vector<int> get_rank() { return rank; }
vector<int> get_lcp() { return lcp; }
// constructor
SuffixArray() {}
SuffixArray(const Str &str_, bool no_limit_elements = false) : str(str_) {
build_sa(no_limit_elements);
}
void init(const Str &str_, bool no_limit_elements = false) {
str = str_;
build_sa(no_limit_elements);
}
void build_sa(bool no_limit_elements = false) {
vector<int> s;
int num_of_chars = 256;
if (!no_limit_elements) {
for (int i = 0; i < (int)str.size(); ++i) {
s.push_back(str[i] + 1);
}
} else {
unordered_map<int, int> dict;
for (int i = 0; i < (int)str.size(); ++i) {
if (!dict.count(str[i])) dict[str[i]] = dict.size();
}
for (int i = 0; i < (int)str.size(); ++i) {
s.push_back(dict[str[i]] + 1);
}
num_of_chars = (int)dict.size();
}
s.push_back(0);
sa = sa_is(s, num_of_chars);
build_lcp(s);
build_sparse_table();
}
// SA-IS
// num_of_chars: # of characters
vector<int> sa_is(vector<int> &s, int num_of_chars) {
int N = (int)s.size();
if (N == 0)
return {};
else if (N == 1)
return {0};
else if (N == 2) {
if (s[0] < s[1])
return {0, 1};
else
return {1, 0};
}
vector<int> isa(N);
vector<bool> ls(N, false);
for (int i = N - 2; i >= 0; --i) {
ls[i] = (s[i] == s[i + 1]) ? ls[i + 1] : (s[i] < s[i + 1]);
}
vector<int> sum_l(num_of_chars + 1, 0), sum_s(num_of_chars + 1, 0);
for (int i = 0; i < N; ++i) {
if (!ls[i])
++sum_s[s[i]];
else
++sum_l[s[i] + 1];
}
for (int i = 0; i <= num_of_chars; ++i) {
sum_s[i] += sum_l[i];
if (i < num_of_chars) sum_l[i + 1] += sum_s[i];
}
auto induce = [&](const vector<int> &lms) -> void {
fill(isa.begin(), isa.end(), -1);
vector<int> buf(num_of_chars + 1);
copy(sum_s.begin(), sum_s.end(), buf.begin());
for (auto d : lms) {
if (d == N) continue;
isa[buf[s[d]]++] = d;
}
copy(sum_l.begin(), sum_l.end(), buf.begin());
isa[buf[s[N - 1]]++] = N - 1;
for (int i = 0; i < N; ++i) {
int v = isa[i];
if (v >= 1 && !ls[v - 1]) {
isa[buf[s[v - 1]]++] = v - 1;
}
}
copy(sum_l.begin(), sum_l.end(), buf.begin());
for (int i = N - 1; i >= 0; --i) {
int v = isa[i];
if (v >= 1 && ls[v - 1]) {
isa[--buf[s[v - 1] + 1]] = v - 1;
}
}
};
vector<int> lms, lms_map(N + 1, -1);
int M = 0;
for (int i = 1; i < N; ++i) {
if (!ls[i - 1] && ls[i]) {
lms_map[i] = M++;
}
}
lms.reserve(M);
for (int i = 1; i < N; ++i) {
if (!ls[i - 1] && ls[i]) {
lms.push_back(i);
}
}
induce(lms);
if (M) {
vector<int> lms2;
lms2.reserve(isa.size());
for (auto v : isa) {
if (lms_map[v] != -1) lms2.push_back(v);
}
int rec_upper = 0;
vector<int> rec_s(M);
rec_s[lms_map[lms2[0]]] = 0;
for (int i = 1; i < M; ++i) {
int l = lms2[i - 1], r = lms2[i];
int nl = (lms_map[l] + 1 < M) ? lms[lms_map[l] + 1] : N;
int nr = (lms_map[r] + 1 < M) ? lms[lms_map[r] + 1] : N;
bool same = true;
if (nl - l != nr - r)
same = false;
else {
while (l < nl) {
if (s[l] != s[r]) break;
++l, ++r;
}
if (l == N || s[l] != s[r]) same = false;
}
if (!same) ++rec_upper;
rec_s[lms_map[lms2[i]]] = rec_upper;
}
auto rec_sa = sa_is(rec_s, rec_upper);
vector<int> sorted_lms(M);
for (int i = 0; i < M; ++i) {
sorted_lms[i] = lms[rec_sa[i]];
}
induce(sorted_lms);
}
return isa;
}
// find min id that str.substr(sa[id]) >= T
int lower_bound(const Str &T) {
int left = -1, right = sa.size();
while (right - left > 1) {
int mid = (left + right) / 2;
if (str.compare(sa[mid], string::npos, T) < 0)
left = mid;
else
right = mid;
}
return right;
}
// find min id that str.substr(sa[id], T.size()) > T
int upper_bound(const Str &T) {
int left = -1, right = sa.size();
while (right - left > 1) {
int mid = (left + right) / 2;
if (str.compare(sa[mid], T.size(), T) <= 0)
left = mid;
else
right = mid;
}
return right;
}
// find min id that sa[id] >= str.substr(l, r-l)
int lower_bound(int l, int r) {
int left = -1, right = rank[l];
while (right - left > 1) {
int mid = (left + right) / 2;
if (st.get(mid, rank[l]) < r - l)
left = mid;
else
right = mid;
}
return right;
}
// search
bool is_contain(const Str &T) {
int lb = lower_bound(T);
if (lb >= sa.size()) return false;
return str.compare(sa[lb], T.size(), T) == 0;
}
// find lcp
void build_lcp(const vector<int> &s) {
int N = (int)s.size();
rank.assign(N, 0), lcp.assign(N - 1, 0);
for (int i = 0; i < N; ++i) rank[sa[i]] = i;
int h = 0;
for (int i = 0; i < N - 1; ++i) {
int pi = sa[rank[i] - 1];
if (h > 0) --h;
for (; pi + h < N && i + h < N; ++h) {
if (s[pi + h] != s[i + h]) break;
}
lcp[rank[i] - 1] = h;
}
}
// build sparse table for calculating lcp
void build_sparse_table() { st.init(lcp); }
// calc lcp of str.sutstr(a) and str.substr(b)
int get_lcp(int a, int b) {
return st.get(min(rank[a], rank[b]), max(rank[a], rank[b]));
}
// debug
void dump() {
for (int i = 0; i < sa.size(); ++i) {
cout << i << ": " << sa[i] << ", " << str.substr(sa[i]) << endl;
}
}
};