競技プログラミング用 C++ ライブラリ
string/dynamic_pattern_matching.hpp · GitHub で見る
共通ページ
固定した本文に対して、パターンを編集しながら出現回数と出現位置を求めます。文字の挿入・削除・置換、部分文字列の削除・移動・コピーに対応します。照合は厳密で、ハッシュの衝突はありません。
#include "lib/string/dynamic_pattern_matching.hpp"
int main() {
DynamicPatternMatching matching("banana", "ana");
assert(matching.count() == 2);
assert((matching.occurrences() == std::vector<int>{3, 1}));
matching.erase(1);
assert(matching.str() == "aa" && matching.count() == 0);
matching.insert(1, 'n');
assert(matching.count() == 2);
auto saved = matching;
matching.copy(0, 3, 3);
assert(matching.str() == "anaana" && matching.count() == 0);
assert(saved.str() == "ana" && saved.count() == 2);
matching.erase(0, 3);
assert(matching.count() == 2);
matching.assign("ban");
matching.move(0, 1, 2);
assert(matching.str() == "anb");
matching.clear();
assert(matching.count() == 7);
matching.compact();
}
本文とパターンは std::string_view で渡します。バイト単位で扱い、NUL を含む全 256 種類に対応します。入力した文字列はコピーするため、呼び出し後に元の文字列を変更・破棄できます。本文は INT_MAX/2 バイト未満、パターン長と位置は uint64_t の範囲です。パターンを編集しても本文は変わりません。本文の変更には build での再構築が必要です。
添字は全て 0 始まり、区間は [l,r) です。insert(p,...) と copy(l,r,p) の挿入位置は、操作前のパターンの p 文字目の直前です。p == size() なら末尾に挿入します。copy は、操作前の [l,r) をコピーするので、p がコピー元の内部でも使えます。
move(l,r,p) の p は 元の区間を削除した後 の位置です。例えば abcdef に move(1,3,4) を行うと、bc を取り除いた adef の位置 4 に挿入し、adefbc になります。0 <= p <= size()-(r-l) を満たしてください。空区間の削除・移動・コピーは何もしません。コピーや挿入の後の長さが UINT64_MAX を超えないようにしてください。
count() は重なりを含めた出現回数です。空のパターンは本文の位置 0~text().size() に現れるため、出現回数は本文長に 1 を足した値です。本文にない文字を含む場合や、パターンが本文より長い場合の出現回数は 0 です。
occurrences() は出現位置を 接尾辞の辞書順 に返します。位置の昇順が必要な場合は返り値をソートしてください。occurrence(i) はその i 番目を配列を作らず取得します。occurrence_range() の [l,r) は本文上の区間ではなく、suffix_array() 上の区間です。出現位置は suffix_array()[l]~suffix_array()[r-1] です。この接尾辞配列は空の接尾辞も含み、長さは本文長に 1 を足した値、先頭は本文長です。
コピー構築・コピー代入では本文と木を共有します。その後の編集はコピー元に影響しないため、auto saved = matching; として状態を保存できます。ムーブ元は本文・パターンとも空になります。文字列を展開する str() はパターン長に比例する出力を作るため、巨大なコピーの後は count()・operator[] などを使ってください。
部分文字列のコピーは木を共有して処理します。編集で不要になった木の節点はその場で破棄せず、記憶領域に残します。clear()・assign() でも節点の記憶領域は残ります。長期間編集を続ける場合は、適宜 compact() で現在の状態から到達できない節点を取り除いてください。保存した別のコピーがある場合、そのコピーの記憶領域は解放されません。storage_nodes() は共有する記憶領域の節点数を返し、現在のパターンの断片数とは異なります。節点番号は内部で int を使うため、保持節点数は INT_MAX 未満にしてください。
String Matching with a Dynamic Pattern の固定本文版を実装しています。本文に出現する断片と、本文にない 1 バイトの断片でパターンを表し、隣り合う断片を結合しても本文に現れない状態を保ちます。編集時は境界付近だけを直します。断片間の結合は接尾辞配列と逆配列で二分探索し、断片の切り出しは LCP 配列上のセグメント木で探索します。これにより RMQ の追加の対数因子を前処理に持ち込まず、線形時間・空間で本文の索引を作ります。木は共有可能な永続 AVL 木です。論文第7節の本文への文字追加は扱いません。
本文長を \(n\)、操作前後のパターン長の最大を \(m\)、渡す文字列の長さを \(s\)、出現回数を \(k\)、共有して保持する節点数を \(A\) とします。対数の項は長さが 0・1 の場合に定数として扱います。† は節点を格納する配列の伸長を含む償却計算量です。
| 操作 | 内容・返り値 | 計算量 |
|---|---|---|
DynamicPatternMatching() |
本文・パターンとも空の状態を作る | \(O(1)\) |
DynamicPatternMatching(text,pattern={}) |
本文の索引と初期パターンを作る | \(O(n+s)\) |
build(text,pattern={}) |
本文とパターンを作り直す | 新規構築 \(O(n+s)\) と旧記憶領域の解放 |
assign(pattern) |
パターンを渡した文字列に置き換える | † \(O(s)\) |
clear() |
パターンを空にする | \(O(1)\) |
size() / empty()
|
パターン長 / パターンが空か | \(O(1)\) |
text() |
固定した本文の string_view
|
\(O(1)\) |
insert(p,c) |
位置 p に 1 バイト挿入 |
† \(O(\log n+\log m)\) |
insert(p,pattern) |
位置 p に文字列を挿入 |
† \(O(s+\log n+\log m)\) |
erase(p) / erase(l,r)
|
1 バイト / 区間を削除 | † \(O(\log n+\log m)\) |
set(p,c) |
位置 p を 1 バイトに置換 |
† \(O(\log n+\log m)\) |
move(l,r,p) |
区間を削除後の位置 p に移動 |
† \(O(\log n+\log m)\) |
copy(l,r,p) |
区間のコピーを操作前の位置 p に挿入 |
† \(O(\log n+\log m)\) |
count() |
出現回数を int で返す |
\(O(1)\) |
occurrence(i) |
接尾辞配列の順で i 番目の出現位置 |
\(O(1)\) |
occurrences() |
全出現位置の vector<int>
|
\(O(k)\) |
occurrence_range() |
出現する接尾辞配列の区間 [l,r)
|
\(O(1)\) |
suffix_array() |
空の接尾辞を含む接尾辞配列への const 参照 | \(O(1)\) |
operator[](p) |
パターンの位置 p のバイト |
\(O(\log m)\) |
str() |
現在のパターン全体を string に展開 |
\(O(m)\) |
storage_nodes() |
共有する節点記憶領域の保持節点数 | \(O(1)\) |
compact() |
本文の索引は共有し、現在必要な節点だけを新しい記憶領域に移す | \(O(A)\) 時間・追加空間 |
| コピー構築 / コピー代入 | 本文・木を共有して状態を保存 | 構築 \(O(1)\)、代入は旧記憶領域の解放が加わる |
| ムーブ構築 / ムーブ代入 | 状態を移し、移動元を空に戻す | 構築 \(O(1)\)、代入は旧記憶領域の解放が加わる |
swap(other) |
状態を交換する | \(O(1)\) |
本文の索引は \(O(n)\) 空間です。初期パターンには高々 \(O(s)\) 節点を使います。文字の編集・部分文字列の操作 1 回につき追加する節点数は \(O(\log m)\)、長さ \(s\) の文字列の代入・挿入ではさらに \(O(s)\) です。最後に共有状態が破棄される際は、その記憶領域の解放が発生します。パターン長を本文長以下に保つ場合、編集の計算量は論文と同じ † \(O(\log n)\) です。
#ifndef DYNAMIC_PATTERN_MATCHING_HPP
#define DYNAMIC_PATTERN_MATCHING_HPP
#include "../components/persistent_rope.hpp"
#include "../components/suffix_range.hpp"
#include <memory>
#include <string>
class DynamicPatternMatching {
struct Piece { SuffixRangeIndex::Range range; int start = 0; };
static std::uint64_t weight(const Piece &piece) { return piece.range.length; }
using Rope = PersistentRope<Piece, weight>;
struct State {
std::shared_ptr<const SuffixRangeIndex> index;
Rope rope;
explicit State(std::shared_ptr<const SuffixRangeIndex> index) : index(std::move(index)) {}
};
std::shared_ptr<State> state_;
int root_ = 0;
Rope &rope() const { return state_->rope; }
const SuffixRangeIndex &index() const { return *state_->index; }
Piece character(char c) const {
auto range = index().character(c);
return {range, range.left < range.right ? index().suffix_array()[range.left] : -int(static_cast<unsigned char>(c)) - 1};
}
Piece subpiece(Piece piece, int l, int r) const {
return {index().substring(piece.range, l, r), piece.start + l};
}
int concat(int a, int b) {
if (!a || !b) return a ? a : b;
auto x = rope().back(a), y = rope().front(b);
auto range = index().concat(x.range, y.range);
if (range.left == range.right) return rope().join(a, b);
int left = rope().pop_back(a).first, right = rope().pop_front(b).second;
int middle = rope().make(Piece{range, index().suffix_array()[range.left]});
return rope().join(rope().join(left, middle), right);
}
std::pair<int, int> split(int root, std::uint64_t p) {
bool divided = false;
auto result = rope().split(root, p, [&](Piece piece, std::uint64_t offset) {
divided = true;
int k = static_cast<int>(offset);
return std::pair{subpiece(piece, 0, k), subpiece(piece, k, piece.range.length)};
});
if (!divided) return result;
auto &[a, b] = result;
if (a && !rope().leaf(a)) {
auto [rest, last] = rope().pop_back(a);
a = concat(rest, rope().make(last));
}
if (b && !rope().leaf(b)) {
auto [first, rest] = rope().pop_front(b);
b = concat(rope().make(first), rest);
}
return result;
}
int make(std::string_view pattern) {
std::vector<Piece> pieces;
while (!pattern.empty()) {
auto [range, length] = index().longest_prefix(pattern);
if (!length) { pieces.push_back(character(pattern.front())); length = 1; }
else pieces.push_back({range, index().suffix_array()[range.left]});
pattern.remove_prefix(length);
}
return rope().build(pieces);
}
void check_range([[maybe_unused]] std::uint64_t l, [[maybe_unused]] std::uint64_t r) const { assert(l <= r && r <= size()); }
void check_growth([[maybe_unused]] std::uint64_t length) const { assert(length <= std::numeric_limits<std::uint64_t>::max() - size()); }
public:
using size_type = std::uint64_t;
DynamicPatternMatching() : DynamicPatternMatching(std::string_view{}) {}
explicit DynamicPatternMatching(std::string_view text, std::string_view pattern = {})
: state_(std::make_shared<State>(std::make_shared<const SuffixRangeIndex>(text))) { root_ = make(pattern); }
DynamicPatternMatching(const DynamicPatternMatching &) = default;
DynamicPatternMatching &operator=(const DynamicPatternMatching &) = default;
DynamicPatternMatching(DynamicPatternMatching &&other) : DynamicPatternMatching() { swap(other); }
DynamicPatternMatching &operator=(DynamicPatternMatching &&other) {
if (this != &other) { DynamicPatternMatching next(std::move(other)); swap(next); }
return *this;
}
void build(std::string_view text, std::string_view pattern = {}) { DynamicPatternMatching next(text, pattern); swap(next); }
void swap(DynamicPatternMatching &other) noexcept { state_.swap(other.state_); std::swap(root_, other.root_); }
void assign(std::string_view pattern) { root_ = make(pattern); }
void clear() { root_ = 0; }
size_type size() const { return rope().size(root_); }
bool empty() const { return root_ == 0; }
std::string_view text() const { return index().text(); }
std::size_t storage_nodes() const { return rope().storage_nodes(); }
void compact() {
auto next = std::make_shared<State>(state_->index);
int root = next->rope.copy_from(rope(), root_);
state_ = std::move(next); root_ = root;
}
void insert(size_type p, char c) {
assert(p <= size()); check_growth(1);
auto [a, b] = split(root_, p);
int middle = rope().make(character(c));
root_ = concat(concat(a, middle), b);
}
void insert(size_type p, std::string_view pattern) {
assert(p <= size()); check_growth(pattern.size());
if (pattern.empty()) return;
int middle = make(pattern);
auto [a, b] = split(root_, p);
root_ = concat(concat(a, middle), b);
}
void erase(size_type p) { assert(p < size()); erase(p, p + 1); }
void erase(size_type l, size_type r) {
check_range(l, r);
if (l == r) return;
auto [a, rest] = split(root_, l);
int b = split(rest, r - l).second;
root_ = concat(a, b);
}
void set(size_type p, char c) { assert(p < size()); erase(p); insert(p, c); }
void move(size_type l, size_type r, size_type p) {
check_range(l, r); assert(p <= size() - (r - l));
if (l == r || p == l) return;
auto [a, rest] = split(root_, l);
auto [middle, b] = split(rest, r - l);
int remaining = concat(a, b);
auto [left, right] = split(remaining, p);
root_ = concat(concat(left, middle), right);
}
void copy(size_type l, size_type r, size_type p) {
check_range(l, r); assert(p <= size()); check_growth(r - l);
if (l == r) return;
int rest = split(root_, l).second, middle = split(rest, r - l).first;
auto [a, b] = split(root_, p);
root_ = concat(concat(a, middle), b);
}
std::pair<int, int> occurrence_range() const {
if (!root_) return {0, index().size() + 1};
if (!rope().leaf(root_)) return {0, 0};
auto range = rope().value(root_).range;
return {range.left, range.right};
}
int count() const { auto [l, r] = occurrence_range(); return r - l; }
int occurrence(int i) const { auto [l, r] = occurrence_range(); assert(0 <= i && i < r - l); return index().suffix_array()[l + i]; }
std::vector<int> occurrences() const { auto [l, r] = occurrence_range(); return {index().suffix_array().begin() + l, index().suffix_array().begin() + r}; }
const std::vector<int> &suffix_array() const { return index().suffix_array(); }
char operator[](size_type p) const {
auto [piece, offset] = rope().at(root_, p);
return piece.start < 0 ? static_cast<char>(-piece.start - 1) : text()[piece.start + offset];
}
std::string str() const {
std::string result;
assert(size() <= result.max_size());
result.reserve(static_cast<std::size_t>(size()));
rope().for_each(root_, [&](Piece piece) {
if (piece.start < 0) result.push_back(static_cast<char>(-piece.start - 1));
else result.append(text().substr(piece.start, piece.range.length));
});
return result;
}
};
#endif
#line 1 "string/dynamic_pattern_matching.hpp"
#line 1 "components/persistent_rope.hpp"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <limits>
#include <utility>
#include <vector>
template<class T, auto weight> class PersistentRope {
struct Node {
std::uint64_t size;
int left, right, height;
T value;
};
std::vector<Node> nodes_{{0, 0, 0, 0, T{}}};
int height(int v) const { return nodes_[v].height; }
int append(Node node) {
assert(nodes_.size() < std::size_t(std::numeric_limits<int>::max()));
nodes_.push_back(std::move(node));
return static_cast<int>(nodes_.size()) - 1;
}
int branch(int a, int b) {
if (!a) return b;
if (!b) return a;
assert(size(a) <= std::numeric_limits<std::uint64_t>::max() - size(b));
return append({size(a) + size(b), a, b, std::max(height(a), height(b)) + 1, T{}});
}
int balance(int a, int b) {
if (height(a) > height(b) + 1) {
int left = nodes_[a].left, right = nodes_[a].right;
if (height(left) >= height(right)) return branch(left, branch(right, b));
int middle_left = nodes_[right].left, middle_right = nodes_[right].right;
return branch(branch(left, middle_left), branch(middle_right, b));
}
if (height(b) > height(a) + 1) {
int left = nodes_[b].left, right = nodes_[b].right;
if (height(right) >= height(left)) return branch(branch(a, left), right);
int middle_left = nodes_[left].left, middle_right = nodes_[left].right;
return branch(branch(a, middle_left), branch(middle_right, right));
}
return branch(a, b);
}
public:
std::uint64_t size(int v) const { return nodes_[v].size; }
std::size_t storage_nodes() const { return nodes_.size() - 1; }
bool leaf(int v) const { return v && !nodes_[v].left; }
T value(int v) const { assert(leaf(v)); return nodes_[v].value; }
int make(T value) {
auto length = std::uint64_t(weight(value));
assert(length);
return append({length, 0, 0, 1, std::move(value)});
}
int join(int a, int b) {
if (!a || !b) return a ? a : b;
if (height(a) > height(b) + 1) {
int left = nodes_[a].left, right = nodes_[a].right;
return balance(left, join(right, b));
}
if (height(b) > height(a) + 1) {
int left = nodes_[b].left, right = nodes_[b].right;
return balance(join(a, left), right);
}
return branch(a, b);
}
int build(const std::vector<T> &values) {
auto visit = [&](auto &&self, std::size_t l, std::size_t r) -> int {
if (l == r) return 0;
if (r - l == 1) return make(values[l]);
auto m = l + (r - l) / 2;
int a = self(self, l, m), b = self(self, m, r);
return branch(a, b);
};
return visit(visit, 0, values.size());
}
template<class Splitter> std::pair<int, int> split(int v, std::uint64_t p, const Splitter &splitter) {
assert(p <= size(v));
if (!p) return {0, v};
if (p == size(v)) return {v, 0};
int left = nodes_[v].left, right = nodes_[v].right;
if (!left) {
T value = nodes_[v].value;
auto [a, b] = splitter(value, p);
int l = make(std::move(a)), r = make(std::move(b));
return {l, r};
}
auto k = size(left);
if (p == k) return {left, right};
if (p < k) {
auto [a, b] = split(left, p, splitter);
return {a, join(b, right)};
}
auto [a, b] = split(right, p - k, splitter);
return {join(left, a), b};
}
int replace(int v, std::uint64_t p, T value) {
assert(p < size(v));
int a = nodes_[v].left, b = nodes_[v].right;
if (!a) { assert(std::uint64_t(weight(value)) == size(v)); return make(std::move(value)); }
if (p < size(a)) a = replace(a, p, std::move(value));
else b = replace(b, p - size(a), std::move(value));
return branch(a, b);
}
template<class Splitter> int insert(int v, std::uint64_t p, T value, const Splitter &splitter) {
assert(p <= size(v));
if (!v) return make(std::move(value));
int a = nodes_[v].left, b = nodes_[v].right;
if (!a) {
if (!p) return branch(make(std::move(value)), v);
if (p == size(v)) return branch(v, make(std::move(value)));
auto [left, right] = split(v, p, splitter);
int middle = make(std::move(value));
return join(join(left, middle), right);
}
if (p <= size(a)) a = insert(a, p, std::move(value), splitter);
else b = insert(b, p - size(a), std::move(value), splitter);
return balance(a, b);
}
template<class Splitter> int erase(int v, std::uint64_t l, std::uint64_t r, const Splitter &splitter) {
assert(l <= r && r <= size(v));
if (l == r) return v;
if (!l && r == size(v)) return 0;
int a = nodes_[v].left, b = nodes_[v].right;
if (!a) {
if (!l) return split(v, r, splitter).second;
if (r == size(v)) return split(v, l, splitter).first;
auto [left, rest] = split(v, l, splitter);
int right = split(rest, r - l, splitter).second;
return join(left, right);
}
auto k = size(a);
if (r <= k) a = erase(a, l, r, splitter);
else if (l >= k) b = erase(b, l - k, r - k, splitter);
else {
a = erase(a, l, k, splitter);
b = erase(b, 0, r - k, splitter);
}
return join(a, b);
}
T front(int v) const { assert(v); while (nodes_[v].left) v = nodes_[v].left; return value(v); }
T back(int v) const { assert(v); while (nodes_[v].right) v = nodes_[v].right; return value(v); }
std::pair<T, int> pop_front(int v) {
assert(v);
int left = nodes_[v].left, right = nodes_[v].right;
if (!left) return {value(v), 0};
auto [value, rest] = pop_front(left);
return {std::move(value), join(rest, right)};
}
std::pair<int, T> pop_back(int v) {
assert(v);
int left = nodes_[v].left, right = nodes_[v].right;
if (!right) return {0, value(v)};
auto [rest, value] = pop_back(right);
return {join(left, rest), std::move(value)};
}
std::pair<T, std::uint64_t> at(int v, std::uint64_t p) const {
assert(p < size(v));
while (int left = nodes_[v].left) {
int right = nodes_[v].right;
auto length = size(left);
if (p < length) v = left;
else p -= length, v = right;
}
return {value(v), p};
}
template<class Visitor> void for_each(int v, const Visitor &visitor) const {
if (!v) return;
if (leaf(v)) { visitor(value(v)); return; }
for_each(nodes_[v].left, visitor);
for_each(nodes_[v].right, visitor);
}
int copy_from(const PersistentRope &other, int root) {
assert(this != &other);
std::vector<int> mapped(other.nodes_.size(), 0);
auto visit = [&](auto &&self, int v) -> int {
if (!v || mapped[v]) return mapped[v];
auto x = other.nodes_[v];
if (x.left) {
x.left = self(self, x.left);
x.right = self(self, x.right);
}
return mapped[v] = append(std::move(x));
};
return visit(visit, root);
}
};
#line 1 "components/suffix_range.hpp"
#line 1 "string/suffix_tree.hpp"
#line 1 "components/suffix_array.hpp"
#line 5 "components/suffix_array.hpp"
#include <array>
#include <bit>
#line 10 "components/suffix_array.hpp"
#include <numeric>
#include <string_view>
#line 13 "components/suffix_array.hpp"
#include <type_traits>
#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__))
#include <immintrin.h>
#endif
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC push_options
#pragma GCC optimize("O3")
#endif
namespace suffix_array_detail {
#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__))
__attribute__((target("avx2"))) inline void byte_seeds(const unsigned char *s, int n, std::vector<int> &seeds, std::vector<unsigned char> &marked) {
seeds.push_back(n), marked[n] = 1;
bool right = true;
int end = n;
const auto sign = _mm256_set1_epi8(static_cast<char>(0x80));
while (end >= 33) {
int begin = end - 32;
auto a = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(s + begin));
auto next = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(s + begin + 1));
auto previous = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(s + begin - 1));
auto biased = _mm256_xor_si256(a, sign);
std::uint32_t equal = _mm256_movemask_epi8(_mm256_cmpeq_epi8(a, next));
std::uint32_t small = _mm256_movemask_epi8(_mm256_cmpgt_epi8(_mm256_xor_si256(next, sign), biased));
if (right) small |= equal & 0x80000000U;
for (int shift : {1, 2, 4, 8, 16}) {
small |= equal & (small >> shift);
equal &= equal >> shift;
}
std::uint32_t selected = small & std::uint32_t(_mm256_movemask_epi8(_mm256_cmpgt_epi8(_mm256_xor_si256(previous, sign), biased)));
while (selected) {
int bit = 31 - std::countl_zero(selected), p = begin + bit;
seeds.push_back(p), marked[p] = 1;
selected ^= std::uint32_t(1) << bit;
}
right = small & 1;
end = begin;
}
for (int i = end - 1; i > 0; --i) {
bool current = s[i] < s[i + 1] || (s[i] == s[i + 1] && right);
if (current && s[i - 1] > s[i]) seeds.push_back(i), marked[i] = 1;
right = current;
}
}
#endif
template<class Sequence>
bool prefix_order(const Sequence &s, int n, int alphabet, const std::vector<int> &seeds, std::vector<int> &ordered) {
if (seeds.size() <= 64 || alphabet > 257) return false;
int bits = std::bit_width(unsigned(alphabet - 1)), position_bits = std::bit_width(unsigned(n));
int length = (64 - position_bits) / bits;
std::uint64_t position_mask = (std::uint64_t(1) << position_bits) - 1;
auto key_at = [&](int p) {
std::uint64_t key = 0;
for (int k = 0; k < length; ++k) key = (key << bits) | (k < n - p ? unsigned(s[p + k]) : 0);
return key;
};
std::array<std::uint64_t, 128> sample;
int sample_size = std::min(128, static_cast<int>(seeds.size()));
for (int i = 0; i < sample_size; ++i) sample[i] = key_at(seeds[i]);
std::sort(sample.begin(), sample.begin() + sample_size);
for (int i = 1; i < sample_size; ++i) if (sample[i] == sample[i - 1]) return false;
std::vector<std::uint64_t> entries(seeds.size()), scratch(seeds.size());
for (std::size_t i = 0; i < seeds.size(); ++i) entries[i] = (key_at(seeds[i]) << position_bits) | unsigned(n - seeds[i]);
int first_shift = std::max(position_bits, bits * length + position_bits - 33);
for (int shift = first_shift; shift < bits * length + position_bits; shift += 11) {
std::array<int, 2048> count{};
for (auto entry : entries) ++count[(entry >> shift) & 2047];
int total = 0;
for (int &value : count) { int next = total + value; value = total; total = next; }
for (auto entry : entries) scratch[count[(entry >> shift) & 2047]++] = entry;
entries.swap(scratch);
}
for (std::size_t first = 0; first < entries.size();) {
std::size_t last = first + 1;
while (last < entries.size() && (entries[last] >> first_shift) == (entries[first] >> first_shift)) ++last;
if (last - first > 64) return false;
std::sort(entries.begin() + first, entries.begin() + last);
for (std::size_t i = first + 1; i < last; ++i) if ((entries[i] >> position_bits) == (entries[i - 1] >> position_bits)) return false;
first = last;
}
ordered.resize(seeds.size());
for (std::size_t i = 0; i < entries.size(); ++i) ordered[i] = n - static_cast<int>(entries[i] & position_mask);
return true;
}
template<class Sequence>
std::vector<int> encoded_sort(const Sequence &s, int n, int alphabet) {
std::vector<int> sa(std::size_t(n) + 1);
if (n < 16) {
std::iota(sa.begin(), sa.end(), 0);
std::sort(sa.begin(), sa.end(), [&](int a, int b) {
while (a < n && b < n && s[a] == s[b]) ++a, ++b;
return a != n ? b != n && s[a] < s[b] : b != n;
});
return sa;
}
std::vector<unsigned char> is_seed(std::size_t(n) + 1);
std::vector<int> seeds;
seeds.reserve(n / 2 + 1);
bool gathered = false;
#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__))
if constexpr (std::is_same_v<Sequence, std::vector<unsigned char>>) {
if (n >= 64 && __builtin_cpu_supports("avx2")) {
byte_seeds(s.data(), n, seeds, is_seed);
gathered = true;
}
}
#endif
if (!gathered) {
bool next = true;
for (int i = n - 1; i >= 0; --i) {
bool current = s[i] == s[i + 1] ? next : s[i] < s[i + 1];
if (!current && next) seeds.push_back(i + 1), is_seed[i + 1] = 1;
next = current;
}
}
std::reverse(seeds.begin(), seeds.end());
std::vector<std::size_t> ends(alphabet), work(alphabet);
for (std::size_t i = 0; i < sa.size(); ++i) ++ends[s[i]];
std::partial_sum(ends.begin(), ends.end(), ends.begin());
auto induce = [&](const std::vector<int> &ordered) {
std::fill(sa.begin(), sa.end(), 0);
work = ends;
for (auto i = ordered.rbegin(); i != ordered.rend(); ++i) if (*i != n) sa[--work[s[*i]]] = *i + 1;
work[0] = 0;
std::copy(ends.begin(), ends.end() - 1, work.begin() + 1);
int last = n;
if (s[n - 2] < s[n - 1]) last = -last;
sa[work[s[n - 1]]++] = last;
for (std::size_t i = 1; i < sa.size(); ++i) {
int p = sa[i];
if (p > 1) {
int j = p - 2, c = s[j], v = j + 1;
if (!j || s[j - 1] < c) v = -v;
sa[work[c]++] = v;
}
}
work = ends;
for (int i = n; i > 0; --i) {
int p = sa[i];
sa[i] = (p < 0 ? -p : p) - 1;
if (p < -1) {
int j = -p - 2, c = s[j], v = j + 1;
if (j && s[j - 1] <= c) v = -v;
sa[--work[c]] = v;
}
}
sa[0] = n;
};
std::vector<int> ordered;
int used = alphabet;
while (used > 1 && ends[used - 1] == ends[used - 2]) --used;
if (prefix_order(s, n, used, seeds, ordered)) { induce(ordered); return sa; }
induce(seeds);
int m = static_cast<int>(seeds.size());
std::vector<int> sorted;
sorted.reserve(m);
for (int p : sa) if (is_seed[p]) sorted.push_back(p);
std::vector<int> names(std::size_t(n) / 2 + 1);
for (int i = 0; i + 1 < m; ++i) names[seeds[i] >> 1] = seeds[i + 1] - seeds[i] + 1;
names[n >> 1] = 1;
int rank = -1, previous = -1, previous_length = 0;
for (int p : sorted) {
int length = names[p >> 1];
bool different = previous < 0 || length != previous_length;
if (!different) for (int k = 0; k < length; ++k) if (s[p + k] != s[previous + k]) { different = true; break; }
rank += different;
names[p >> 1] = rank;
previous = p, previous_length = length;
}
std::vector<int> reduced(m), order(m);
for (int i = 0; i < m; ++i) reduced[i] = names[seeds[i] >> 1];
if (rank + 1 == m) for (int i = 0; i < m; ++i) order[reduced[i]] = i;
else order = encoded_sort(reduced, m - 1, rank + 1);
for (int i = 0; i < m; ++i) sorted[i] = seeds[order[i]];
induce(sorted);
return sa;
}
template<class Sequence>
std::vector<int> induced_sort(const Sequence &s, int upper) {
struct Encoded {
const Sequence &value;
int n;
int operator[](int i) const { return i == n ? 0 : value[i] + 1; }
};
int n = static_cast<int>(s.size());
if (n < 2) return n ? std::vector<int>{0} : std::vector<int>{};
int common = 1;
while (common < n && s[common] == s[0]) ++common;
if (common == n) {
std::vector<int> sa(n);
std::iota(sa.rbegin(), sa.rend(), 0);
return sa;
}
auto sa = encoded_sort(Encoded{s, n}, n, upper + 2);
sa.erase(sa.begin());
return sa;
}
inline std::vector<int> bytes(std::string_view s) {
assert(s.size() <= std::size_t(std::numeric_limits<int>::max()));
if (s.empty()) return {};
std::array<int, 256> codes{};
for (unsigned char c : s) codes[c] = 1;
int alphabet = 0;
for (int &code : codes) if (code) code = ++alphabet;
int n = static_cast<int>(s.size());
if (alphabet == 1) {
std::vector<int> sa(n);
std::iota(sa.rbegin(), sa.rend(), 0);
return sa;
}
auto build = [&]<class Symbol>() {
std::vector<Symbol> text(s.size() + 1);
for (int i = 0; i < n; ++i) text[i] = static_cast<Symbol>(codes[static_cast<unsigned char>(s[i])]);
auto sa = encoded_sort(text, n, alphabet + 1);
sa.erase(sa.begin());
return sa;
};
return alphabet < 256 ? build.template operator()<unsigned char>() : build.template operator()<std::uint16_t>();
}
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC pop_options
#endif
#line 1 "components/sequence_compare.hpp"
#line 5 "components/sequence_compare.hpp"
#include <cstddef>
#line 7 "components/sequence_compare.hpp"
#include <cstring>
#line 9 "components/sequence_compare.hpp"
#if defined(__x86_64__)
#include <immintrin.h>
#endif
namespace sequence_compare_detail {
#if defined(__x86_64__)
__attribute__((target("avx2"))) inline std::size_t forward_avx2(const unsigned char *a, const unsigned char *b, std::size_t size) {
std::size_t i = 0;
for (; size - i >= 32; i += 32) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + i));
auto y = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(b + i));
auto difference = ~std::uint32_t(_mm256_movemask_epi8(_mm256_cmpeq_epi8(x, y)));
if (difference) return i + std::countr_zero(difference);
}
while (i < size && a[i] == b[i]) ++i;
return i;
}
__attribute__((target("avx2"))) inline std::size_t backward_avx2(const unsigned char *a, const unsigned char *b, std::size_t size) {
std::size_t i = 0;
for (; size - i >= 32; i += 32) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a - i - 32));
auto y = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(b - i - 32));
auto difference = ~std::uint32_t(_mm256_movemask_epi8(_mm256_cmpeq_epi8(x, y)));
if (difference) return i + std::countl_zero(difference);
}
while (i < size && a[-std::ptrdiff_t(i) - 1] == b[-std::ptrdiff_t(i) - 1]) ++i;
return i;
}
#endif
template<bool Vectorize = true, class T>
inline int prefix(const T *first, const T *second, int limit) {
if (!limit) return 0;
int matched = 0;
if constexpr (std::is_integral_v<T> && std::has_unique_object_representations_v<T>) {
const auto *a = reinterpret_cast<const unsigned char *>(first);
const auto *b = reinterpret_cast<const unsigned char *>(second);
std::size_t offset = 0, size = std::size_t(limit) * sizeof(T);
while (size - offset >= 8) {
std::uint64_t x, y;
std::memcpy(&x, a + offset, 8);
std::memcpy(&y, b + offset, 8);
auto difference = x ^ y;
if (difference) {
int bits = std::endian::native == std::endian::little ? std::countr_zero(difference) : std::countl_zero(difference);
return static_cast<int>((offset + bits / 8) / sizeof(T));
}
offset += 8;
#if defined(__x86_64__)
if constexpr (Vectorize) if (size - offset >= 64 && __builtin_cpu_supports("avx2"))
return static_cast<int>((offset + forward_avx2(a + offset, b + offset, size - offset)) / sizeof(T));
#endif
}
matched = static_cast<int>(offset / sizeof(T));
}
while (matched < limit && first[matched] == second[matched]) ++matched;
return matched;
}
template<bool Vectorize = true, class T>
inline int suffix(const T *first_end, const T *second_end, int limit) {
if (!limit) return 0;
int matched = 0;
if constexpr (std::is_integral_v<T> && std::has_unique_object_representations_v<T>) {
const auto *a = reinterpret_cast<const unsigned char *>(first_end);
const auto *b = reinterpret_cast<const unsigned char *>(second_end);
std::size_t offset = 0, size = std::size_t(limit) * sizeof(T);
while (size - offset >= 8) {
std::uint64_t x, y;
std::memcpy(&x, a - offset - 8, 8);
std::memcpy(&y, b - offset - 8, 8);
auto difference = x ^ y;
if (difference) {
int bits = std::endian::native == std::endian::little ? std::countl_zero(difference) : std::countr_zero(difference);
return static_cast<int>((offset + bits / 8) / sizeof(T));
}
offset += 8;
#if defined(__x86_64__)
if constexpr (Vectorize) if (size - offset >= 64 && __builtin_cpu_supports("avx2"))
return static_cast<int>((offset + backward_avx2(a - offset, b - offset, size - offset)) / sizeof(T));
#endif
}
matched = static_cast<int>(offset / sizeof(T));
}
while (matched < limit && first_end[-matched - 1] == second_end[-matched - 1]) ++matched;
return matched;
}
}
#line 1 "components/static_range_min.hpp"
#line 1 "components/default_init_allocator.hpp"
#include <memory>
#include <new>
template <class T>
struct DefaultInitAllocator : std::allocator<T> {
template <class U>
struct rebind {
using other = DefaultInitAllocator<U>;
};
DefaultInitAllocator() = default;
template <class U>
DefaultInitAllocator(const DefaultInitAllocator<U> &) noexcept {}
template <class U>
void construct(U *p) { ::new (static_cast<void *>(p)) U; }
};
#line 1 "data_structure/sparse_table.hpp"
#line 14 "data_structure/sparse_table.hpp"
template <class S, auto op, auto e = nullptr>
class SparseTable {
using Value = std::conditional_t<std::is_same_v<S, bool>, std::uint8_t, S>;
using Reference = std::conditional_t<std::is_same_v<S, bool>, bool, const S &>;
int n = 0;
std::vector<std::vector<Value, DefaultInitAllocator<Value>>> data;
static decltype(auto) combine(const Value &a, const Value &b) {
if constexpr (std::is_same_v<S, bool> || !std::is_invocable_v<decltype(op), const S &, const S &>) return S(op(S(a), S(b)));
else return op(a, b);
}
public:
SparseTable() = default;
SparseTable(const SparseTable &) = default;
SparseTable &operator=(const SparseTable &) = default;
SparseTable(SparseTable &&other) noexcept : n(std::exchange(other.n, 0)), data(std::move(other.data)) {}
SparseTable &operator=(SparseTable &&other) noexcept {
if (this != &other) {
n = std::exchange(other.n, 0);
data = std::move(other.data);
}
return *this;
}
explicit SparseTable(const std::vector<S> &a) { build(a); }
void clear() {
data.clear();
n = 0;
}
void build(const std::vector<S> &a) {
assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
build(static_cast<int>(a.size()), [&](int i) -> S { return a[i]; });
}
template <class F>
void build(int size, F value) {
assert(size >= 0);
n = size;
data.resize(std::bit_width(unsigned(n)));
if (!n) return;
if constexpr (std::is_default_constructible_v<Value>) {
data[0].resize(n);
for (int i = 0; i < n; ++i) data[0][i] = value(i);
} else {
data[0].clear();
data[0].reserve(n);
for (int i = 0; i < n; ++i) data[0].push_back(value(i));
}
for (int k = 1; k < std::bit_width(unsigned(n)); ++k) {
int length = n - (1U << k) + 1, half = 1U << (k - 1);
if constexpr (std::is_default_constructible_v<Value>) {
data[k].resize(length);
for (int i = 0; i < length; ++i) data[k][i] = combine(data[k - 1][i], data[k - 1][i + half]);
} else {
data[k].clear();
data[k].reserve(length);
for (int i = 0; i < length; ++i) data[k].push_back(combine(data[k - 1][i], data[k - 1][i + half]));
}
}
}
int size() const { return n; }
bool empty() const { return n == 0; }
Reference operator[](int i) const {
assert(0 <= i && i < n);
return data[0][i];
}
[[gnu::always_inline]] S prod(int l, int r) const {
assert(0 <= l && l <= r && r <= n);
if constexpr (std::is_same_v<decltype(e), std::nullptr_t>) assert(l < r);
else if (l == r) return e();
return prod_unchecked(l, r);
}
[[gnu::always_inline]] S prod_unchecked(int l, int r) const {
int k = std::bit_width(unsigned(r - l)) - 1;
return combine(data[k][l], data[k][r - (1U << k)]);
}
S all_prod() const { return prod(0, n); }
};
#line 12 "components/static_range_min.hpp"
#include <span>
#line 16 "components/static_range_min.hpp"
template <class T = int, bool FastBoundaries = false> class StaticRangeMin {
static T minimum(T a, T b) { return b < a ? b : a; }
std::vector<T> values_;
std::vector<std::uint32_t, DefaultInitAllocator<std::uint32_t>> masks_;
SparseTable<T, minimum> blocks_;
struct Boundaries { std::vector<T> prefix, suffix; };
struct Empty {};
[[no_unique_address]] std::conditional_t<FastBoundaries, Boundaries, Empty> boundaries_;
T within(int l, int r) const {
auto mask = masks_[r - 1] & (~std::uint32_t(0) << (l & 31));
return values_[(l & ~31) + std::countr_zero(mask)];
}
public:
StaticRangeMin() = default;
explicit StaticRangeMin(std::vector<T> values) { build(std::move(values)); }
void build(std::vector<T> values) {
assert(values.size() <= std::size_t(std::numeric_limits<int>::max()));
values_ = std::move(values);
int n = size();
masks_.resize(n);
for (int first = 0; first < n;) {
int last = first + std::min(32, n - first);
std::uint32_t mask = 0;
for (int i = first; i < last; ++i) {
while (mask && !(values_[first + std::bit_width(mask) - 1] < values_[i]))
mask &= ~(std::uint32_t(1) << (std::bit_width(mask) - 1));
mask |= std::uint32_t(1) << (i - first);
masks_[i] = mask;
}
first = last;
}
if constexpr (FastBoundaries) {
boundaries_.prefix = values_, boundaries_.suffix = values_;
for (int first = 0; first < n;) {
int last = first + std::min(32, n - first);
for (int i = first + 1; i < last; ++i) boundaries_.prefix[i] = minimum(boundaries_.prefix[i - 1], values_[i]);
for (int i = last - 1; i > first; --i) boundaries_.suffix[i - 1] = minimum(values_[i - 1], boundaries_.suffix[i]);
first = last;
}
}
blocks_.build(n ? (n - 1) / 32 + 1 : 0, [&](int i) {
int last = i * 32 + std::min(32, n - i * 32);
if constexpr (FastBoundaries) return T(boundaries_.prefix[last - 1]);
else return within(i * 32, last);
});
}
int size() const { return static_cast<int>(values_.size()); }
bool empty() const { return values_.empty(); }
std::conditional_t<std::is_same_v<T, bool>, bool, const T &> operator[](int i) const {
assert(0 <= i && i < size());
return values_[i];
}
T prod(int l, int r) const {
assert(0 <= l && l < r && r <= size());
int a = l / 32, b = (r - 1) / 32;
if (a == b) return within(l, r);
T value = [&] {
if constexpr (FastBoundaries) return minimum(boundaries_.suffix[l], boundaries_.prefix[r - 1]);
else return minimum(within(l, (a + 1) * 32), within(b * 32, r));
}();
if (a + 1 < b) value = minimum(value, blocks_.prod_unchecked(a + 1, b));
return value;
}
std::vector<T> prod_many(std::span<const std::pair<int, int>> ranges) const {
std::vector<T> result;
result.reserve(ranges.size());
if constexpr (FastBoundaries) {
for (auto [l, r] : ranges) result.emplace_back(prod(l, r));
} else {
std::array<int, 32> left, right;
for (std::size_t first = 0; first < ranges.size(); first += 32) {
int count = int(std::min<std::size_t>(32, ranges.size() - first));
for (int i = 0; i < count; ++i) {
auto [l, r] = ranges[first + i];
assert(0 <= l && l < r && r <= size());
int a = l & ~31, b = (r - 1) & ~31;
auto mask = masks_[a == b ? r - 1 : a + 31] & (~std::uint32_t(0) << (l & 31));
left[i] = a + std::countr_zero(mask);
right[i] = a == b ? left[i] : b + std::countr_zero(masks_[r - 1]);
#if defined(__GNUC__) || defined(__clang__)
if constexpr (!std::is_same_v<T, bool>) {
__builtin_prefetch(values_.data() + left[i]);
__builtin_prefetch(values_.data() + right[i]);
}
#endif
}
for (int i = 0; i < count; ++i) {
auto [l, r] = ranges[first + i];
T value = minimum(values_[left[i]], values_[right[i]]);
int a = l / 32 + 1, b = (r - 1) / 32;
if (a < b) value = minimum(value, blocks_.prod_unchecked(a, b));
result.emplace_back(std::move(value));
}
}
}
return result;
}
T all_prod() const { return prod(0, size()); }
};
#line 9 "string/suffix_tree.hpp"
#include <optional>
#line 11 "string/suffix_tree.hpp"
#include <string>
#line 13 "string/suffix_tree.hpp"
class SuffixTree {
struct Node { int parent, depth, left, right; };
std::string text_;
mutable std::vector<Node> nodes_;
std::vector<int> sa_;
mutable std::vector<int> lcp_, rank_;
mutable std::vector<int> terminals_, branches_, offset_, children_;
mutable std::vector<unsigned char> symbols_;
mutable std::array<int, 256> root_children_{};
mutable bool lcp_ready_ = false, tree_ready_ = false;
mutable std::uint64_t distinct_ = 0;
mutable std::optional<StaticRangeMin<std::pair<int, int>>> index_;
void check([[maybe_unused]] int v) const { prepare_tree(); assert(0 <= v && v < static_cast<int>(nodes_.size())); }
int range_end(int v) const { int r = nodes_[v].right; return r < 0 ? nodes_[-r - 1].right : r; }
void prepare_lcp() const {
if (lcp_ready_) return;
int n = size();
rank_.resize(n + 1);
for (int i = 0; i <= n; ++i) rank_[sa_[i]] = i;
lcp_.assign(n, 0);
for (int p = 0, h = 0; p < n; ++p) {
if (h) --h;
int i = rank_[p], q = sa_[i - 1];
h += sequence_compare_detail::prefix(text_.data() + p + h, text_.data() + q + h, std::min(n - p, n - q) - h);
lcp_[i - 1] = h;
}
distinct_ = std::uint64_t(n) * (n + 1) / 2;
for (int value : lcp_) distinct_ -= value;
lcp_ready_ = true;
}
void prepare_tree() const {
if (tree_ready_) return;
prepare_lcp();
int n = size();
nodes_.clear();
nodes_.reserve(std::size_t(n) + 1);
nodes_.push_back({-1, 0, 0, n + 1});
terminals_.assign(n + 1, 0);
branches_.resize(n);
std::vector<int> stack{0};
for (int i = 1; i <= n; ++i) {
int common = lcp_[i - 1], last = -1;
while (nodes_[stack.back()].depth > common) { last = stack.back(); nodes_[last].right = i; stack.pop_back(); }
if (nodes_[stack.back()].depth < common) {
int v = static_cast<int>(nodes_.size());
nodes_.push_back({stack.back(), common, nodes_[last].left, n + 1});
nodes_[last].parent = v;
stack.push_back(v);
}
branches_[i - 1] = stack.back();
int v = static_cast<int>(nodes_.size()), p = sa_[i];
nodes_.push_back({stack.back(), n - p, i, n + 1});
terminals_[p] = v;
stack.push_back(v);
}
int count = static_cast<int>(nodes_.size());
offset_.assign(nodes_.size() + 1, 0);
for (int v = 1; v < count; ++v) ++offset_[nodes_[v].parent + 1];
for (int v = 0; v < count; ++v) offset_[v + 1] += offset_[v];
children_.resize(nodes_.size() - 1);
symbols_.resize(children_.size());
auto cursor = offset_;
for (int v = 1; v < count; ++v) {
int p = nodes_[v].parent, i = cursor[p]++;
children_[i] = v;
symbols_[i] = static_cast<unsigned char>(text_[sa_[nodes_[v].left] + nodes_[p].depth]);
}
root_children_.fill(-1);
for (int i = offset_[0]; i < offset_[1]; ++i) {
int v = children_[i];
root_children_[static_cast<unsigned char>(text_[sa_[nodes_[v].left]])] = v;
}
for (int i = n; i >= 0; --i) {
int v = terminals_[sa_[i]];
if (offset_[v + 1] - offset_[v] == 1) {
int u = children_[offset_[v]];
nodes_[v].right = nodes_[u].right < 0 ? nodes_[u].right : -u - 1;
}
}
tree_ready_ = true;
}
void prepare_index() const {
prepare_tree();
if (index_) return;
std::vector<std::pair<int, int>> values(size());
for (int i = 0; i < size(); ++i) values[i] = {lcp_[i], branches_[i]};
index_.emplace(std::move(values));
}
int child(int v, unsigned char c) const {
if (!v) return root_children_[c];
int l = offset_[v], r = offset_[v + 1];
if (r - l <= 4) {
for (int i = l; i < r; ++i) if (symbols_[i] == c) return children_[i];
return -1;
}
int end = r;
while (l < r) { int m = l + (r - l) / 2; if (symbols_[m] < c) l = m + 1; else r = m; }
return l < end && symbols_[l] == c ? children_[l] : -1;
}
public:
struct Locus { int node, length; };
static constexpr int root = 0;
SuffixTree() { build({}); }
explicit SuffixTree(std::string_view s) { build(s); }
SuffixTree(const SuffixTree &) = default;
SuffixTree &operator=(const SuffixTree &) = default;
SuffixTree(SuffixTree &&other) : SuffixTree() { swap(other); }
SuffixTree &operator=(SuffixTree &&other) {
if (this != &other) { SuffixTree moved(std::move(other)); swap(moved); }
return *this;
}
void swap(SuffixTree &other) noexcept {
text_.swap(other.text_);
nodes_.swap(other.nodes_);
sa_.swap(other.sa_); lcp_.swap(other.lcp_); rank_.swap(other.rank_);
terminals_.swap(other.terminals_); branches_.swap(other.branches_);
offset_.swap(other.offset_); children_.swap(other.children_);
symbols_.swap(other.symbols_);
root_children_.swap(other.root_children_);
std::swap(distinct_, other.distinct_);
std::swap(lcp_ready_, other.lcp_ready_);
std::swap(tree_ready_, other.tree_ready_);
index_.swap(other.index_);
}
void build(std::string_view s) {
assert(s.size() < std::size_t(std::numeric_limits<int>::max() / 2));
std::string copy(s);
text_.swap(copy);
int n = size();
sa_ = suffix_array_detail::bytes(text_);
sa_.insert(sa_.begin(), n);
rank_.clear(); lcp_.clear();
lcp_ready_ = false;
nodes_.clear(); terminals_.clear(); branches_.clear(); offset_.clear(); children_.clear(); symbols_.clear();
tree_ready_ = false;
index_.reset();
}
void clear() { build({}); }
int size() const { return static_cast<int>(text_.size()); }
bool empty() const { return text_.empty(); }
int node_count() const { prepare_tree(); return static_cast<int>(nodes_.size()); }
std::string_view text() const { return text_; }
int parent(int v) const { check(v); return nodes_[v].parent; }
int depth(int v) const { check(v); return nodes_[v].depth; }
std::pair<int, int> edge_range(int v) const {
check(v);
if (!v) return {0, 0};
int p = sa_[nodes_[v].left];
return {p + nodes_[nodes_[v].parent].depth, p + nodes_[v].depth};
}
std::pair<int, int> label_range(int v) const { check(v); int p = sa_[nodes_[v].left]; return {p, p + nodes_[v].depth}; }
std::span<const int> children(int v) const {
check(v);
int count = offset_[v + 1] - offset_[v];
return count ? std::span<const int>(children_.data() + offset_[v], count) : std::span<const int>{};
}
int transition(int v, char c) const { check(v); return child(v, static_cast<unsigned char>(c)); }
int suffix_start(int v) const { check(v); int p = sa_[nodes_[v].left]; return size() - p == nodes_[v].depth ? p : -1; }
int suffix_node(int p) const { prepare_tree(); assert(0 <= p && p <= size()); return terminals_[p]; }
std::pair<int, int> occurrence_range(int v) const { check(v); return {nodes_[v].left, range_end(v)}; }
int occurrence_count(int v) const { check(v); return range_end(v) - nodes_[v].left; }
const std::vector<int> &suffix_array() const { return sa_; }
const std::vector<int> &lcp_array() const { prepare_lcp(); return lcp_; }
std::uint64_t distinct_substrings() const { prepare_lcp(); return distinct_; }
std::optional<Locus> find(std::string_view pattern) const {
prepare_tree();
if (pattern.size() > text_.size()) return std::nullopt;
int v = 0, matched = 0, length = static_cast<int>(pattern.size());
while (matched < length) {
int u = child(v, static_cast<unsigned char>(pattern[matched]));
if (u == -1) return std::nullopt;
if (nodes_[u].right >= 0) {
int start = sa_[nodes_[u].left] + matched;
int take = std::min(length - matched, nodes_[u].depth - matched);
if (sequence_compare_detail::prefix(text_.data() + start, pattern.data() + matched, take) != take) return std::nullopt;
matched += take;
v = u;
continue;
}
int end = -nodes_[u].right - 1;
int start = sa_[nodes_[end].left] + nodes_[v].depth;
int take = std::min(length - matched, nodes_[end].depth - nodes_[v].depth);
if (sequence_compare_detail::prefix(text_.data() + start, pattern.data() + matched, take) != take) return std::nullopt;
matched += take;
if (matched < nodes_[end].depth && nodes_[u].depth < matched) {
int l = nodes_[u].left + 1, r = std::min(nodes_[end].left, nodes_[u].left + matched - nodes_[u].depth);
while (l < r) { int m = l + (r - l) / 2; if (size() - sa_[m] < matched) l = m + 1; else r = m; }
v = l == nodes_[end].left ? end : terminals_[sa_[l]];
} else v = matched <= nodes_[u].depth ? u : end;
}
return Locus{v, length};
}
std::optional<Locus> advance(Locus locus, char c) const {
check(locus.node);
int v = locus.node;
assert((!v && locus.length == 0) || (v && nodes_[nodes_[v].parent].depth < locus.length && locus.length <= nodes_[v].depth));
if (locus.length < nodes_[v].depth) {
if (text_[sa_[nodes_[v].left] + locus.length] != c) return std::nullopt;
return Locus{v, locus.length + 1};
}
int u = child(v, static_cast<unsigned char>(c));
return u == -1 ? std::nullopt : std::optional<Locus>(Locus{u, locus.length + 1});
}
bool contains(std::string_view pattern) const { return find(pattern).has_value(); }
int count(std::string_view pattern) const { auto locus = find(pattern); return locus ? occurrence_count(locus->node) : 0; }
std::vector<int> occurrences(std::string_view pattern) const {
auto locus = find(pattern);
if (!locus) return {};
auto [l, r] = occurrence_range(locus->node);
return {sa_.begin() + l, sa_.begin() + r};
}
int lcp(int p, int q) const {
assert(0 <= p && p <= size() && 0 <= q && q <= size());
if (p == q) return size() - p;
prepare_lcp();
int a = rank_[p], b = rank_[q];
if (a > b) std::swap(a, b);
prepare_index();
return index_->prod(a, b).first;
}
int lca(int u, int v) const {
check(u); check(v);
int end_u = range_end(u), end_v = range_end(v);
if (nodes_[u].left <= nodes_[v].left && end_v <= end_u) return u;
if (nodes_[v].left <= nodes_[u].left && end_u <= end_v) return v;
int a = nodes_[u].left, b = nodes_[v].left;
if (a > b) std::swap(a, b);
prepare_index();
return index_->prod(a, b).second;
}
int suffix_link(int v) const {
check(v);
if (!v) return 0;
int p = suffix_start(v);
if (p >= 0) return terminals_[p + 1];
return lca(terminals_[sa_[nodes_[v].left] + 1], terminals_[sa_[range_end(v) - 1] + 1]);
}
};
#line 5 "components/suffix_range.hpp"
class SuffixRangeIndex {
SuffixTree tree_;
std::vector<int> rank_, minimum_;
int capacity_ = 1;
int first_below(int v, int l, int r, int ql, int qr, int length) const {
if (r <= ql || qr <= l || minimum_[v] >= length) return -1;
if (r - l == 1) return l;
int m = l + (r - l) / 2;
int a = first_below(v * 2, l, m, ql, qr, length);
return a == -1 ? first_below(v * 2 + 1, m, r, ql, qr, length) : a;
}
int last_below(int v, int l, int r, int ql, int qr, int length) const {
if (r <= ql || qr <= l || minimum_[v] >= length) return -1;
if (r - l == 1) return l;
int m = l + (r - l) / 2;
int a = last_below(v * 2 + 1, m, r, ql, qr, length);
return a == -1 ? last_below(v * 2, l, m, ql, qr, length) : a;
}
public:
struct Range { int left = 0, right = 0, length = 0; };
SuffixRangeIndex() : SuffixRangeIndex(std::string_view{}) {}
explicit SuffixRangeIndex(std::string_view text) : tree_(text) {
int n = tree_.size();
rank_.resize(n + 1);
for (int i = 0; i <= n; ++i) rank_[suffix_array()[i]] = i;
while (capacity_ < n) capacity_ *= 2;
minimum_.assign(std::size_t(capacity_) * 2, std::numeric_limits<int>::max());
std::copy(tree_.lcp_array().begin(), tree_.lcp_array().end(), minimum_.begin() + capacity_);
for (int i = capacity_ - 1; i; --i) minimum_[i] = std::min(minimum_[i * 2], minimum_[i * 2 + 1]);
}
int size() const { return tree_.size(); }
std::string_view text() const { return tree_.text(); }
const std::vector<int> &suffix_array() const { return tree_.suffix_array(); }
Range empty_range() const { return {0, size() + 1, 0}; }
Range character(char c) const {
int v = tree_.transition(0, c);
if (v == -1) return {0, 0, 1};
auto [l, r] = tree_.occurrence_range(v);
return {l, r, 1};
}
Range concat(Range a, Range b) const {
if (!a.length) return b;
if (!b.length) return a;
assert(a.length <= std::numeric_limits<int>::max() - b.length);
Range missing{0, 0, a.length + b.length};
if (a.left == a.right || b.left == b.right || a.length > size() - b.length) return missing;
auto bound = [&](int target, int l) {
int r = a.right;
while (l < r) {
int m = l + (r - l) / 2;
if (rank_[suffix_array()[m] + a.length] < target) l = m + 1;
else r = m;
}
return l;
};
int l = bound(b.left, a.left), r = bound(b.right, l);
return l == r ? missing : Range{l, r, a.length + b.length};
}
Range substring(Range range, int l, int r) const {
assert(0 <= l && l <= r && r <= range.length && range.left < range.right);
if (l == r) return empty_range();
int i = rank_[suffix_array()[range.left] + l], length = r - l;
int a = last_below(1, 0, capacity_, 0, i, length) + 1;
int b = first_below(1, 0, capacity_, i, size(), length);
return {a, b == -1 ? size() + 1 : b + 1, length};
}
std::pair<Range, int> longest_prefix(std::string_view pattern) const {
SuffixTree::Locus locus{0, 0};
for (char c : pattern) {
auto next = tree_.advance(locus, c);
if (!next) break;
locus = *next;
}
auto [l, r] = tree_.occurrence_range(locus.node);
return {Range{l, r, locus.length}, locus.length};
}
};
#line 8 "string/dynamic_pattern_matching.hpp"
class DynamicPatternMatching {
struct Piece { SuffixRangeIndex::Range range; int start = 0; };
static std::uint64_t weight(const Piece &piece) { return piece.range.length; }
using Rope = PersistentRope<Piece, weight>;
struct State {
std::shared_ptr<const SuffixRangeIndex> index;
Rope rope;
explicit State(std::shared_ptr<const SuffixRangeIndex> index) : index(std::move(index)) {}
};
std::shared_ptr<State> state_;
int root_ = 0;
Rope &rope() const { return state_->rope; }
const SuffixRangeIndex &index() const { return *state_->index; }
Piece character(char c) const {
auto range = index().character(c);
return {range, range.left < range.right ? index().suffix_array()[range.left] : -int(static_cast<unsigned char>(c)) - 1};
}
Piece subpiece(Piece piece, int l, int r) const {
return {index().substring(piece.range, l, r), piece.start + l};
}
int concat(int a, int b) {
if (!a || !b) return a ? a : b;
auto x = rope().back(a), y = rope().front(b);
auto range = index().concat(x.range, y.range);
if (range.left == range.right) return rope().join(a, b);
int left = rope().pop_back(a).first, right = rope().pop_front(b).second;
int middle = rope().make(Piece{range, index().suffix_array()[range.left]});
return rope().join(rope().join(left, middle), right);
}
std::pair<int, int> split(int root, std::uint64_t p) {
bool divided = false;
auto result = rope().split(root, p, [&](Piece piece, std::uint64_t offset) {
divided = true;
int k = static_cast<int>(offset);
return std::pair{subpiece(piece, 0, k), subpiece(piece, k, piece.range.length)};
});
if (!divided) return result;
auto &[a, b] = result;
if (a && !rope().leaf(a)) {
auto [rest, last] = rope().pop_back(a);
a = concat(rest, rope().make(last));
}
if (b && !rope().leaf(b)) {
auto [first, rest] = rope().pop_front(b);
b = concat(rope().make(first), rest);
}
return result;
}
int make(std::string_view pattern) {
std::vector<Piece> pieces;
while (!pattern.empty()) {
auto [range, length] = index().longest_prefix(pattern);
if (!length) { pieces.push_back(character(pattern.front())); length = 1; }
else pieces.push_back({range, index().suffix_array()[range.left]});
pattern.remove_prefix(length);
}
return rope().build(pieces);
}
void check_range([[maybe_unused]] std::uint64_t l, [[maybe_unused]] std::uint64_t r) const { assert(l <= r && r <= size()); }
void check_growth([[maybe_unused]] std::uint64_t length) const { assert(length <= std::numeric_limits<std::uint64_t>::max() - size()); }
public:
using size_type = std::uint64_t;
DynamicPatternMatching() : DynamicPatternMatching(std::string_view{}) {}
explicit DynamicPatternMatching(std::string_view text, std::string_view pattern = {})
: state_(std::make_shared<State>(std::make_shared<const SuffixRangeIndex>(text))) { root_ = make(pattern); }
DynamicPatternMatching(const DynamicPatternMatching &) = default;
DynamicPatternMatching &operator=(const DynamicPatternMatching &) = default;
DynamicPatternMatching(DynamicPatternMatching &&other) : DynamicPatternMatching() { swap(other); }
DynamicPatternMatching &operator=(DynamicPatternMatching &&other) {
if (this != &other) { DynamicPatternMatching next(std::move(other)); swap(next); }
return *this;
}
void build(std::string_view text, std::string_view pattern = {}) { DynamicPatternMatching next(text, pattern); swap(next); }
void swap(DynamicPatternMatching &other) noexcept { state_.swap(other.state_); std::swap(root_, other.root_); }
void assign(std::string_view pattern) { root_ = make(pattern); }
void clear() { root_ = 0; }
size_type size() const { return rope().size(root_); }
bool empty() const { return root_ == 0; }
std::string_view text() const { return index().text(); }
std::size_t storage_nodes() const { return rope().storage_nodes(); }
void compact() {
auto next = std::make_shared<State>(state_->index);
int root = next->rope.copy_from(rope(), root_);
state_ = std::move(next); root_ = root;
}
void insert(size_type p, char c) {
assert(p <= size()); check_growth(1);
auto [a, b] = split(root_, p);
int middle = rope().make(character(c));
root_ = concat(concat(a, middle), b);
}
void insert(size_type p, std::string_view pattern) {
assert(p <= size()); check_growth(pattern.size());
if (pattern.empty()) return;
int middle = make(pattern);
auto [a, b] = split(root_, p);
root_ = concat(concat(a, middle), b);
}
void erase(size_type p) { assert(p < size()); erase(p, p + 1); }
void erase(size_type l, size_type r) {
check_range(l, r);
if (l == r) return;
auto [a, rest] = split(root_, l);
int b = split(rest, r - l).second;
root_ = concat(a, b);
}
void set(size_type p, char c) { assert(p < size()); erase(p); insert(p, c); }
void move(size_type l, size_type r, size_type p) {
check_range(l, r); assert(p <= size() - (r - l));
if (l == r || p == l) return;
auto [a, rest] = split(root_, l);
auto [middle, b] = split(rest, r - l);
int remaining = concat(a, b);
auto [left, right] = split(remaining, p);
root_ = concat(concat(left, middle), right);
}
void copy(size_type l, size_type r, size_type p) {
check_range(l, r); assert(p <= size()); check_growth(r - l);
if (l == r) return;
int rest = split(root_, l).second, middle = split(rest, r - l).first;
auto [a, b] = split(root_, p);
root_ = concat(concat(a, middle), b);
}
std::pair<int, int> occurrence_range() const {
if (!root_) return {0, index().size() + 1};
if (!rope().leaf(root_)) return {0, 0};
auto range = rope().value(root_).range;
return {range.left, range.right};
}
int count() const { auto [l, r] = occurrence_range(); return r - l; }
int occurrence(int i) const { auto [l, r] = occurrence_range(); assert(0 <= i && i < r - l); return index().suffix_array()[l + i]; }
std::vector<int> occurrences() const { auto [l, r] = occurrence_range(); return {index().suffix_array().begin() + l, index().suffix_array().begin() + r}; }
const std::vector<int> &suffix_array() const { return index().suffix_array(); }
char operator[](size_type p) const {
auto [piece, offset] = rope().at(root_, p);
return piece.start < 0 ? static_cast<char>(-piece.start - 1) : text()[piece.start + offset];
}
std::string str() const {
std::string result;
assert(size() <= result.max_size());
result.reserve(static_cast<std::size_t>(size()));
rope().for_each(root_, [&](Piece piece) {
if (piece.start < 0) result.push_back(static_cast<char>(-piece.start - 1));
else result.append(text().substr(piece.start, piece.range.length));
});
return result;
}
};