問題の意味
ストリング、ストリングの各尋ねる所与\([S_L、S_R] \ ) 元の文字列の\(K \) CIの位置が配置されている(ヘッド位置)が発生
ソリューション
質問の意味は、非常にシンプルなアイデアは非常に簡単です
コードは巨人と戦うことは困難です
この質問短いです、私のために多くの食べ物ましょう\(SAM \)問題の理解をするだけでなく、ビット・セグメントツリーの合併を学ぶために
まず、私たちが話題を知っているサフィックスオートマトンの性質は、実際に必要とされます
\([S_L、S_R] \ ) 後置オートマトンが表すノードの\(endposの\)のコレクション\(K \)番号
インターバル探している\(k個\)が大きいですか?重セグメントツリーの友人
私たちは知っている、サフィックスオートマトン上のノードのために、それは\(endposの\)コレクションがあること、それ\(親\)ツリーとすべての人の息子\(endposの\)の収集と
我々は尊重\(親\)をツリーラインの重みを開いて、ツリー内の各ノードについて、各プレフィックスは、その末尾の位置に初期化されます
だから我々は、特定のノードを要求したいと思います\(endposの\)行に限り、彼の息子の権利は、値のセグメントツリーに組み込まれるように上記の収集、
すべての単一のセグメントツリーの重みを動的に開いポイントがあるスペースを、保存するには
だから我々は、サブストリングクエリんか\([S_L、S_R] \ ) 元の文字列のサフィックスオートマトンにノードは、それに対応して?
私たちは、木の乗数を使用することができます
ノード番号を対応する第一の前処理は、すべてのプレフィクス、構築\(親\)ツリー、アレイプリ乗算\(F \)すべてのノードをマージし、決定されたセグメントツリーを\(endposの\)セット
次に、クエリは\([S_L、S_R] \ ) 、我々は最初に見つけ\([1、S_R] \ ) に対応するノードをして、ジャンプを掛けるようになりました
\([S_L、S_R] \ ) 対応するノードの最長文字列はによって表されなければならない\([X、S〜のR] \) 、我々は最大見つける必要が\(X \)よう\([S_L、S_R]を\ )である\([xは、S_R] \ ) サフィックス
分析を倍にすることができます。(ジャンプするので実際には、我々はすべてのバイナリプロセスとして理解乗算し続けることができ、その答えに再び近い\(FA \)プロセスで\(LEN \)は単調減少です)
あなたは、そのノードの値線分の木の上で右クエリ内の対応するノードを見つける場合には\(k個\)が大きくなります
コード
彼らは巨大な遊びよく理解し、カプセル化
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e6 + 10;
int T, n, q;
char a[N];
struct segTree {
int sz;
int ls[N << 2], rs[N << 2], val[N << 2];
void clear() {
sz = 0;
}
void update(int cur) {
val[cur] = val[ls[cur]] + val[rs[cur]];
}
int newnode() {
++sz;
val[sz] = ls[sz] = rs[sz] = 0;
return sz;
}
void mkchain(int& cur, int l, int r, int k) {
if (!cur) cur = newnode();
if (l == r) return val[cur]++, void();
int mid = l + r >> 1;
if (k <= mid)
mkchain(ls[cur], l, mid, k);
else
mkchain(rs[cur], mid + 1, r, k);
update(cur);
}
int merge(int x1, int x2) {
if (!x1 || !x2) return x1 + x2;
int x = ++sz;
ls[x] = merge(ls[x1], ls[x2]);
rs[x] = merge(rs[x1], rs[x2]);
val[x] = val[x1] + val[x2];
return x;
}
int query(int cur, int l, int r, int k) {
if (l == r)
return l;
int mid = l + r >> 1;
if (k <= val[ls[cur]])
return query(ls[cur], l, mid, k);
else if (k <= val[cur])
return query(rs[cur], mid + 1, r, k - val[ls[cur]]);
else
return -1;
}
} tr;
struct SAM {
int sz, lst;
int len[N], fa[N], ch[N][30];
int pos[N], dep[N], rt[N], f[N][30];
int cap;
int head[N], to[N << 1], nxt[N << 1];
void clear() {
sz = lst = 1, cap = 0;
memset(head, 0, sizeof head);
memset(len, 0, sizeof len);
memset(fa, 0, sizeof fa);
memset(ch, 0, sizeof ch);
memset(rt, 0, sizeof rt);
}
void add(int x, int y) {
to[++cap] = y, nxt[cap] = head[x], head[x] = cap;
}
void insert(int po, int c) {
int cur = ++sz, p = lst;
pos[po] = cur, len[cur] = po;
for (; p && !ch[p][c]; p = fa[p]) ch[p][c] = cur;
if (!p)
fa[cur] = 1;
else {
int q = ch[p][c];
if (len[q] == len[p] + 1) fa[cur] = q;
else {
int nq = ++sz;
fa[nq] = fa[q], len[nq] = len[p] + 1;
for (; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
memcpy(ch[nq], ch[q], sizeof ch[q]);
fa[q] = fa[cur] = nq;
}
}
lst = cur;
tr.mkchain(rt[cur], 1, n, po);
}
void DFS(int cur) {
for (int i = 1; i <= 20; ++i) f[cur][i] = f[f[cur][i - 1]][i - 1];
for (int i = head[cur]; i; i = nxt[i]) {
f[to[i]][0] = cur;
DFS(to[i]);
rt[cur] = tr.merge(rt[cur], rt[to[i]]);
}
}
void link() {
for (int i = 2; i <= sz; ++i) add(fa[i], i);
DFS(1);
}
int solve(int l, int r, int k) {
int cur = pos[r];
for (int i = 20; i >= 0; --i) {
int p = f[cur][i];
if (l + len[p] - 1 >= r) cur = p;
}
int ans = tr.query(rt[cur], 1, n, k);
return (ans == -1) ? ans : ans - (r - l);
}
} sam;
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &q);
scanf("%s", a + 1);
sam.clear(), tr.clear();
for (int i = 1; i <= n; ++i) sam.insert(i, a[i] - 'a' + 1);
sam.link();
int l, r, k;
while (q--) {
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", sam.solve(l, r, k));
}
}
return 0;
}