// [1, n] 直接传 s template<int mod = 1000000993, int base = 10333, class T = std::string> struct Hash { std::vector<longlong> power, key; Hash(const T& s){// 直接传 s power.resize(1, 1); key.resize(1, 0); extend(s); }
voidextend(const T& s){ for(int i = 0; i < s.size(); i++){ power.push_back(power.back() * base % mod); key.push_back((key.back() * base + s[i]) % mod); } }
voidpop(){ power.pop_back(); key.pop_back(); }
intget(int l, int r)const{// 下标从 1 开始 return ((key[r] - key[l - 1] * power[r - l + 1]) % mod + mod) % mod; } };
// [1, n] 直接传 s vector<int> exkmp(string s){ int n = s.size(); s = "#" + s; vector<int> z(n + 1); z[1] = n; for (int i = 2, l = 1, r = 1; i <= n; i++) { if (i <= r) { z[i] = min(z[i - l + 1], r - i + 1); }
while (i + z[i] <= n && s[1 + z[i]] == s[i + z[i]]) { z[i]++; }
if (i + z[i] - 1 > r) { l = i; r = i + z[i] - 1; } } return z; }
structSuffix_Array { vector<int> p, rank; Suffix_Array(string s) { int n = s.size(), k = 0; p.resize(n); rank.resize(n); vector<int> q, count; for (int i = 0; i < n; i += 1) p[i] = i; ranges::sort(p, {}, [&](int i) { return s[i]; }); for (int i = 0; i < n; i += 1) rank[p[i]] = i and s[p[i]] == s[p[i - 1]] ? rank[p[i - 1]] : k++; for (int m = 1; m < n; m *= 2) { q.resize(m); for (int i = 0; i < m; i += 1) q[i] = n - m + i; for (int i : p) if (i >= m) q.push_back(i - m); count.assign(k, 0); for (int i : rank) count[i] += 1; for (int i = 1; i < k; i += 1) count[i] += count[i - 1]; for (int i = n - 1; i >= 0; i -= 1) p[count[rank[q[i]]] -= 1] = q[i]; auto cur = rank; cur.resize(2 * n, -1); k = 0; for (int i = 0; i < n; i += 1) rank[p[i]] = i and cur[p[i]] == cur[p[i - 1]] and cur[p[i] + m] == cur[p[i - 1] + m] ? rank[p[i - 1]] : k++; } } };
structSuffix_Array { vector<int> p, rank, height; Suffix_Array(string s) { int n = s.size(), k = 0; p.resize(n); rank.resize(n); height.resize(n); vector<int> q, count; for (int i = 0; i < n; i++) p[i] = i; ranges::sort(p, {}, [&](int i) { return s[i]; }); for (int i = 0; i < n; i++) rank[p[i]] = i and s[p[i]] == s[p[i - 1]] ? rank[p[i - 1]] : k++; for (int m = 1; m < n; m <<= 1) { q.resize(m); for (int i = 0; i < m; i++) q[i] = n - m + i; for (int i : p) if (i >= m) q.push_back(i - m); count.assign(k, 0); for (int i : rank) count[i] += 1; for (int i = 1; i < k; i++) count[i] += count[i - 1]; for (int i = n - 1; i >= 0; i--) p[count[rank[q[i]]] -= 1] = q[i]; auto cur = rank; cur.resize(2 * n, -1); k = 0; for (int i = 0; i < n; i++) rank[p[i]] = i and cur[p[i]] == cur[p[i - 1]] and cur[p[i] + m] == cur[p[i - 1] + m] ? rank[p[i - 1]] : k++; }
for (int i = 0, c = 0; i < n; i++) { if (rank[i]) { if (c) c--; int j = p[rank[i] - 1]; while (i + c < n && j + c < n && s[i + c] == s[j + c]) c++; height[rank[i]] = c; } } } };
template<int N = 26, int OFFSET = -'a'> struct AcAutomaton { struct Node { int cnt; Node* prev; Node* next[N]; vector<int> info;// 记录字符串 id vector<Node*> tree;// 记录 prev 来源,建树 Node() { cnt = 0; prev = 0; for (int i = 0; i < N; i++) { next[i] = 0; } } };
int n; Node* root; AcAutomaton(const vector<string>& vs) { n = vs.size(); root = newNode(); for (int i = 0; i < vs.size(); i++) { insert(vs[i], i); } build(); }
voidinsert(const string& s, int id){ Node* cur = root; for (auto& x : s) { if (cur->next[x + OFFSET] == 0) { cur->next[x + OFFSET] = newNode(); } cur = cur->next[x + OFFSET]; } cur->info.push_back(id); }
voidbuild(){ queue<Node*> q; root->prev = root; for (int i = 0; i < N; i++) { if (root->next[i] != 0) { root->tree.push_back(root->next[i]);// build fail tree root->next[i]->prev = root; q.push(root->next[i]); } else { root->next[i] = root; } }
while (!q.empty()) { auto cur = q.front(); q.pop(); for (int i = 0; i < N; i++) { if (cur->next[i] != 0) { cur->prev->next[i]->tree.push_back(cur->next[i]);// build fail tree cur->next[i]->prev = cur->prev->next[i]; q.push(cur->next[i]); } else { cur->next[i] = cur->prev->next[i]; } } } }
vector<int> count(const string& s){ vector<int> res(n); auto cur = root; for (int i = 0; i < s.size(); i++) { cur = cur->next[s[i] + OFFSET]; cur->cnt += 1; }
auto dfs = [&](auto self, auto cur) -> void { for (auto& v : cur->tree) { self(self, v); cur->cnt += v->cnt; }
for (auto& id : cur->info) { res[id] = cur->cnt; } }; dfs(dfs, root);
structAcAutomaton { int N; int OFFSET; int n; int all; vector<int> fail, cnt; vector<vector<int>> ch; AcAutomaton(const vector<string>& vs, int _N = 26, int _OFFSET = -'a') { N = _N; OFFSET = _OFFSET;
n = 0; all = 0; for (auto& x : vs) { n += x.size(); }
fail.resize(n + 1); cnt.resize(n + 1); ch.resize(n + 1); for (int i = 0; i <= n; i++) { ch[i].resize(N); }
for (auto& x : vs) { insert(x); }
build(); }
voidinsert(const string& s){ int p = 0; for (auto& x : s) { if (ch[p][x + OFFSET] == 0) { ch[p][x + OFFSET] = ++all; } p = ch[p][x + OFFSET]; } cnt[p] += 1; }
voidbuild(){ queue<int> q; for (int i = 0; i < N; i++) {// # if (ch[0][i]) { q.push(ch[0][i]); } }
while (!q.empty()) { auto p = q.front(); q.pop(); for (int i = 0; i < N; i++) { if (ch[p][i] == 0) { ch[p][i] = ch[fail[p]][i]; } else { fail[ch[p][i]] = ch[fail[p]][i]; cnt[ch[p][i]] += cnt[ch[fail[p]][i]]; q.push(ch[p][i]); } } } } };
template<classT> structFenwick { int N; vector<T> C, S; Fenwick(int n = 0) { resize(n); }
voidresize(int n){ N = n; C.resize(N); S.resize(N); }
intlowbit(int x){ return x & (-x); }
voidadd(int p, T k){ for (int i = p; i < N; i += lowbit(i)) { C[i] += k; S[i] += k * p; } }
voidupdate(int l, int r, T k){ add(l, + k); add(r + 1, -k); }
T prefixsum(int p){ T Csum = 0; T Ssum = 0; for (int i = p; i > 0; i -= lowbit(i)) { Csum += C[i] * (p + 1); Ssum += S[i]; } return Csum - Ssum; }
T rangesum(int l, int r){ returnprefixsum(r) - prefixsum(l - 1); } };
structAcAutomaton { constint N; constint OFFSET; int n; int all; vector<int> cnt; vector<int> fail; vector<vector<int>> ch; // other func int m; // vs size vector<int> id;// ith string -> the id of trie node vector<vector<int>> tree; // fail tree vector<int> dfn;// the dfn of fail tree node vector<int> siz;// ths size of subtree Fenwick<longlong> f;// the cnt of fail tree node AcAutomaton(const vector<string>& vs, int _N = 26, int _OFFSET = -'a') : N(_N), OFFSET(_OFFSET) { n = 0; all = 0; m = vs.size(); for (auto& x : vs) { n += x.size(); }
cnt.resize(n + 1); fail.resize(n + 1); ch.resize(n + 1); for (int i = 0; i <= n; i++) { ch[i].resize(N); }
id.resize(m);// m tree.resize(n + 1); dfn.resize(n + 1); siz.resize(n + 1, 1);// init with 1 f.resize(n + 1);
for (int i = 0; i < vs.size(); i++) { insert(vs[i], i); } build(); all = 0;// reset all to 0 dfs(); }
voidinsert(const string& s, int i){ int p = 0; for (auto& x : s) { if (ch[p][x + OFFSET] == 0) { ch[p][x + OFFSET] = ++all; } p = ch[p][x + OFFSET]; } cnt[p] += 1; id[i] = p; }
voidbuild(){ queue<int> q; for (int i = 0; i < N; i++) { if (ch[0][i]) { tree[0].push_back(ch[0][i]); q.push(ch[0][i]); } }
while (!q.empty()) { auto p = q.front(); q.pop(); for (int i = 0; i < N; i++) { if (ch[p][i]) { fail[ch[p][i]] = ch[fail[p]][i]; q.push(ch[p][i]); cnt[ch[p][i]] += cnt[ch[fail[p]][i]]; tree[ch[fail[p]][i]].push_back(ch[p][i]); } else { ch[p][i] = ch[fail[p]][i]; } } } }
voiddfs(int u = 0){ dfn[u] = all++;// all++ if (dfn[u]) { f.update(dfn[u], dfn[u], cnt[u]); } for (auto& v : tree[u]) { dfs(v); siz[u] += siz[v]; } }
voidup(int i){ int l = dfn[id[i]]; int r = dfn[id[i]] + siz[id[i]] - 1; f.update(l, r, + 1); }
voiddn(int i){ int l = dfn[id[i]]; int r = dfn[id[i]] + siz[id[i]] - 1; f.update(l, r, - 1); }
longlongget(const string& s){ longlong ans = 0; for (int i = 0, p = 0; i < s.size(); i++) { p = ch[p][s[i] + OFFSET]; ans += f.rangesum(dfn[p], dfn[p]); } return ans; } };
intinsert(int lst, int c){ int cur = node[lst].next[c]; // if (node[cur].len) return cur; node[cur].len = node[lst].len + 1; int pre = node[lst].link; // 和SAM 不一样 while (pre && !node[pre].next[c]) { node[pre].next[c] = cur; pre = node[pre].link; }
if (pre == 0) { node[cur].link = 1; } else { int pos = node[pre].next[c]; if (node[pos].len == node[pre].len + 1) { node[cur].link = pos; } else { int newpos = newNode(); node[newpos].len = node[pre].len + 1; node[newpos].link = node[pos].link; node[pos].link = newpos; node[cur].link = newpos; while (pre && node[pre].next[c] == pos) { node[pre].next[c] = newpos; pre = node[pre].link; } for (int i = 0; i < ALPHABET_SIZE; i++) { if (node[node[pos].next[i]].len) { // 不会指向空 node[newpos].next[i] = node[pos].next[i]; } else { node[newpos].next[i] = 0; } } } } return cur; }
// 一、建立字典树 voidinsert(const string& s, int offset = -'a'){ int p = 1; for (auto& x : s) { int c = x + offset; if (node[p].next[c] == 0) { node[p].next[c] = newNode(); } p = node[p].next[c]; } }
// 二、字典树上拓扑建立SAM voidbuild(){ queue<pair<int, int>> q; for (int i = 0; i < ALPHABET_SIZE; i++) { if (node[1].next[i]) { q.push({ i, 1 }); } }
while (!q.empty()) { auto [c, lst] = q.front(); q.pop(); int cur = insert(lst, c); for (int i = 0; i < ALPHABET_SIZE; i++) { if (node[cur].next[i]) { q.push({ i, cur }); } } } } };
int& next(int p, int c){ return node[p].next[c]; }
voidextend(const string& s){ for (int i = 0; i < s.size(); i++) { extend(i, s[i]); } reset(); }
voidreset(){ suff = 0; vc.clear(); }
voidextend(int p, char x, char offset = 'a'){ int c = x - offset; vc += c; int pre = getLink(p, suff); int cur = next(pre, c); if (cur == 0) { cur = newNode(); len(cur) = len(pre) + 2; link(cur) = next(getLink(p, link(pre)), c); next(pre, c) = cur;
structSequenceAutomaton { staticconstexprint ALPHABET_SIZE = 26; structNode { int next[ALPHABET_SIZE]; Node() : next{} {} }; vector<Node> node; SequenceAutomaton(const string& s, int offset = -'a') { int n = s.size(); node.resize(n + 1); for (int i = n - 1; i >= 0; i--) { for (int j = 0; j < ALPHABET_SIZE; j++) { node[i].next[j] = node[i + 1].next[j]; } node[i].next[s[i] + offset] = i + 1; } }
boolcount(const string& t, int offset = -'a')const{ int p = 0; int i = 0; while (i < t.size() && node[p].next[t[i] + offset] != 0) { p = node[p].next[t[i] + offset]; i++; } return i == t.size(); } };
structSequenceAutomaton { structNode { unordered_map<int, int> next; Node() : next{} {} }; vector<Node> node; SequenceAutomaton(const string& s) { int n = s.size(); node.resize(n + 1); for (int i = n - 1; i >= 0; i--) { node[i].next = node[i + 1].next; node[i].next[s[i]] = i + 1; } }
boolcount(const string& t)const{ int p = 0; int i = 0; while (i < t.size() && node[p].next.count(t[i])) { p = node[p].next.at(t[i]); i++; } return i == t.size(); } };
Minimal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// [0, n) intminimal(auto s){ int n = s.size(); int i = 0, j = 1, k = 0; while (k < n && i < n && j < n) { if (s[(i + k) % n] == s[(j + k) % n]) { k++; } else { s[(i + k) % n] > s[(j + k) % n] ? i += k + 1 : j += k + 1; if (i == j) j++; k = 0; } } returnmin(i, j); }