いくつかの基本的な文字列操作

無限のピットを埋め、少しポイントがあります。

我々はいくつかの文字列関連の話題だけでなく、口胡とバギーコードの説明を記録するためにここになります。配置ストライキを分割します。

WCコースウェアのコピーの多くもが、コードは非常に友好的ではないかもしれません

T1

長さに\(N \の当量10 ^ 5 \) 文字列、各サブクエリ各接頭文字列failツリーの深さの各ノードの深さ及び根の-1としない回答に含まれます。failツリーは、ある構成ツリーへのポインタ。KMPfail

この時間は、それぞれの各サフィックスプレフィックスのための答えになることを求めて、違いを行って最初に考えてみましょうfailノードとツリーの深さ。

補助定理1:各列failノードとツリーの深さ、および文字列に元の接頭文字列の各々における出現数(また、マイナス\(N- \) )。非常に明らかに結論。

補題1のアプリケーションは、(同じ位置が存在しない)数の各プレフィックスストリング同じ要求となります。

ダブルディファレンシャル以上の何か、次のようになります。各プレフィックス番号の各元の文字列のサフィックスを見つけて、このプレフィックスではなく、元の場所に表示されます。

この事はハラールの多くのように見えます

その後、抗プレフィックスツリーを求めることにより、どのようにこの事を検討してください。各プレフィックス番号は、新興国や求めています。もちろん、それは、文字の各挿入した後、親木をジャンプアップされ、その後、各ノードに答えを追加し\(右\)サイズを掛けるのコレクションMax(s)-Max(p)このことは、ライン上の列の第1の演算ゼロ数の一部の保守のためのプレフィックスツリーツリーライン項目に文字列全体で合わせ、次いで激しくマーキングすることができます。

/* programed by white-55kai */
#if 0
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<utility>
#include<stdlib.h>
#include<time.h>
#else
#include<bits/stdc++.h>
#endif

#if 0
#include<cmath>
#endif

#define REP(i,l,r) for (reg int i=(l);i<=(r);++i)
#define RREP(i,r,l) for (int i=(r);i>=(l);--i)
#define rep(i,l,r) for (int i=(l);i<(r);++i)
#define rrep(i,r,l) for (int i=(r);i>(l);--i)
#define foredge(i,u) for (int i=la[u];i;i=ne[i])
#define mem(a) memset(a,0,sizeof(a))
#define memid(a) memset(a,63,sizeof(a))
#define memax(a) memset(a,127,sizeof(a))
#define dbg(x) cout<<#x<<" = "<<x<<endl
#define reg register
#define tpn typename
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

template <tpn A> inline A Max(const A &x,const A &y){
    return x>y?x:y;
}
template <tpn A> inline A Min(const A &x,const A &y){
    return x<y?x:y;
}
template <tpn A> inline void Swap(A &x,A &y){
    x^=y,y^=x,x^=y;
}
template <tpn A> inline A Abs(const A &x){
    return x>0?x:-x;
}
#if 1
template <tpn A> inline void read(A &x){
    char c;
    A neg=1;
    do{
        c=getchar();
    }while ((c<'0'||c>'9')&&c!='-');
    if (c=='-') neg=-1,c=getchar();
    x=0;
    do{
        x=x*10+c-48;
        c=getchar();
    }while (c>='0'&&c<='9');
    x*=neg;
}
template <tpn A,tpn B> inline void read(A &a,B &b){
read(a),read(b);}
template <tpn A,tpn B,tpn C> inline void read(A &a,B &b,C &c){
read(a),read(b),read(c);}
template <tpn A,tpn B,tpn C,tpn D> inline void read(A &a,B &b,C &c,D &d){
read(a),read(b),read(c),read(d);}
template <tpn A> inline void put(const A &tmp){
    A x=tmp;
    if (x==0){
        putchar('0');
        return;
    }
    if (x<0) putchar('-'),x=-x;
    char buf[30];
    int buf_size=0;
    while (x){
        buf[++buf_size]=x%10+48;
        x/=10;
    }
    RREP(i,buf_size,1) putchar(buf[i]);
}
#else
namespace fastIO {
    #define buf_size 100000
    #define LL long long
    bool error;
    inline char gc() {
        static char buf[buf_size + 1], *l = buf, *r = buf;
        if (l == r) {
            l = buf;
            r = buf + fread(buf, 1, buf_size, stdin);
            if (l == r) {
                error = 1;
                return -1;
            }
        }
        return *l ++;
    }
    inline bool blank(char ch) {
        return ch == '\n' || ch == '\t' || ch == ' ' || ch == '\r' || error;
    }
    inline bool read_int(int &x) {
        char ch;
        int f = 1;
        while (blank(ch = gc()));
        if (error) return false;
        x = 0;
        if (ch == '-') f = -1, ch = gc();
        while (1) {
            x = (x << 1) + (x << 3) + ch - '0';
            if (!isdigit(ch = gc())) break;
        }
        x *= f;
        return true;
    }
    inline bool read_LL(LL &x) {
        char ch;
        LL f = 1;
        while (blank(ch = gc()));
        if (error) return false;
        x = 0;
        if (ch == '-') f = -1, ch = gc();
        while (1) {
            x = (x << 1) + (x << 3) + ch - '0';
            if (!isdigit(ch = gc())) break;
        }
        x *= f;
        return true;
    }
    inline bool read_char(char &x) {
        char ch;
        while (blank(ch = gc()));
        if (error) return false;
        x = ch;
        return true;
    }
    inline void put_int(int x) {
        if (!x) {
            putchar('0');
            return;
        }
        if (x < 0) {
            x = -x;
            putchar('-');
        }
        static int out[13];
        int len = 0;
        while (x) {
            out[++ len] = x % 10;
            x /= 10;
        }
        while (len) putchar(out[len --] + '0');
    }
    inline void put_LL(LL x) {
        if (!x) {
            putchar('0');
            return;
        }
        if (x < 0) {
            x = -x;
            putchar('-');
        }
        static LL out[20];
        int len = 0;
        while (x) {
            out[++ len] = x % 10;
            x /= 10;
        }
        while (len) putchar(out[len --] + '0');
    }
    #undef buf_size
    #undef LL
}
using namespace fastIO;
#endif
inline int mul_mod(int a,int b,int mo){
    int ret;
    __asm__ __volatile__ ("\tmull %%ebx\n\tdivl %%ecx\n":"=d"(ret):"a"(a),"b"(b),"c"(mo));
    return ret;
}
const int N = 100005, p = 1000000007;
char s[N];
int n;
namespace segment {
    struct node {
        int l, r, lson, rson;
        int a, d, siz;
    };
    node tree[N * 18];
    int cnt;
    int build(int l, int r, int x) {
        int now = ++cnt;
        tree[now].l = l, tree[now].r = r, tree[now].siz = 1;
        if (l == r) return now;
        int mid = l + r >> 1;
        if (x <= mid) tree[now].lson = build(l, mid, x);
        else tree[now].rson = build(mid + 1, r, x);
        return now;
    }
    inline void pushdown(int k) {
        static int ls, rs;
        ls = tree[k].lson, rs = tree[k].rson;
        (tree[ls].a += tree[k].a) %= p;
        (tree[ls].d += tree[k].d) %= p;
        tree[rs].a += (tree[k].a + (ll)tree[ls].siz * tree[k].d % p) % p;
        tree[rs].d += tree[k].d;
        tree[rs].a %= p, tree[rs].d %= p;
        tree[k].a = tree[k].d = 0;
    }
    int merge(int le, int ri) {
        if (!le) return ri;
        if (!ri) return le;
        if (tree[le].a || tree[le].d) pushdown(le);
        if (tree[ri].a || tree[ri].d) pushdown(ri);
        tree[le].lson = merge(tree[le].lson, tree[ri].lson);
        tree[le].rson = merge(tree[le].rson, tree[ri].rson);
        tree[le].siz += tree[ri].siz;
        return le;
    }
    void add(int rt, int val) {
        tree[rt].d += val;
        tree[rt].d %= p;
    }
    void query(int rt, int *a) {
        if (tree[rt].l == tree[rt].r) {
            *(a + tree[rt].l) = tree[rt].a;
            return;
        }
        if (tree[rt].a || tree[rt].d) pushdown(rt);
        query(tree[rt].lson, a);
        query(tree[rt].rson, a);
    }
}
int la[N << 1], en[N << 1], ne[N << 1], top;
inline void add(int x,int y){
    ne[++top] = la[x];
    en[top] = y;
    la[x] = top;
}
struct SAM {
    int len[N << 1], par[N << 1], ch[N << 1][26];
    int root[N << 1];
    int lp, p, np, q, nq, sz;
    SAM() { lp = sz = 1; }
    inline void insert(int x) {
        len[np = ++sz] = len[p = lp] + 1;
        par[lp = np] = 1;
        for (; p; p = par[p]) {
            if (!ch[p][x]) ch[p][x] = np;
            else {
                q = ch[p][x];
                if (len[q] == len[p] + 1) par[np] = q;
                else {
                    nq = ++sz;
                    len[nq] = len[p] + 1;
                    par[nq] = par[q];
                    par[q] = par[np] = nq;
                    memcpy(ch[nq], ch[q], sizeof(ch[q]));
                    for (; p && ch[p][x] == q; p = par[p]) ch[p][x] = nq;
                }
                return;
            }
        }
    }
    inline void build() {
        p = 1;
        REP(i, 1, n) {
            p = ch[p][s[i] - 'a'];
            root[p] = segment::build(1, n, i);
        }
        REP(i, 2, sz) add(par[i], i);
    }
    void dfs(int u) {
        foredge(i, u) {
            dfs(en[i]);
            root[u] = segment::merge(root[u], root[en[i]]);
        }
        if (u == 1) return;
        segment::add(root[u], len[u] - len[par[u]]);
    }
    inline void getans() { dfs(1); }
};
SAM van;
int a[N];
int main(){
    read(n);
    scanf("%s", s + 1);
    REP(i, 1, n) van.insert(s[i] - 'a');
    van.build();
    van.getans();
    segment::query(van.root[1], a);
    REP(i, 1, n) a[i] = (a[i] + a[i - 1]) % p;
    REP(i, 1, n) {
        a[i] = (a[i] + a[i - 1]) % p;
        printf("%d\n", a[i]);
    }
    return 0;
}

T2

オリジナルタイトル:CF700E(複数の経験)

文字列が与えられません\(T \)もはやよりも、(^。2 \ 10 AST 5 \)\、Q \(K \)が存在している最大の数です\(S_1、\ ldots、S_K \)を、文字列を満たしますある\(T \)サブおよび\(\ FORALL iは[IN \ N、2]、S_I \) で\(S_は{I-1} \) は、少なくとも2回に分けて表示されます。

まず、条件となる検索\を(\私は\ FORALL [ 2、n]は、S_I \) である(S_ {I-1} \ない \) されているborder回答に影響を。

次に、どのように考えるSAMコックはこの事を尋ねました。仮定しない(T \)\とバックプレフィックスツリー非圧縮(トランスプレフィックスTrie、我々が設定された)f[x]からの第1のシーケンスを表すx文字列のルートが逆プレフィックスツリーをもたらすこのノードに移動した場合の最大\(K \)

場合は、最初、\(Y \)のために転送することができる(X \)を\、それがある(以下、(R(X)を\)\ノードを表すright場所セットに)\(\ R&LT(Y)をEXISTS \ [R&LTで(X)-len(X)+ LEN(Y)は、R(X)-1] \) そうここで、非圧縮がないのでlenこと、各ノード一つだけ)のためのy文字列が表すx表す文字列をサブサフィックスではありません。明らかに、この状態は、ノードから満たされるx鎖からなる根の深さ以上のすべてのノードに。

そして、非常に明らかにf[x]それは深さが増加しています。したがって、転送は最も深い譲渡で最高のノードでなければなりません。明らかに、各ノードは父親から試してみてくださいf。この時点であれば、同じ浅節転移を、その後、転送することができないf[x]=f[fa[x]]、または他f[x]=f[pos[fa[x]]]+1前記pos[x]表し浅いノード番号と同じ値。xf

さて、通常の抗プレフィックスツリーを考えます。各点は、に対応する\([分(X)、 MAX(X)] \) 期間間隔。場合、と結論付けているxからyfa[y]、そのある文字列間の転送xからmin(y)間隔は任意ことが、転送を表すx表示最長部分文字列のローカル代表的には、そこに少なくとも二つになりmin(y)、そしてため(\ [分(Y)、MAX( Y)]を\) 同じノードに属する、すべてmin(y)のオカレンスをに拡張することができmax(y)、したがってmax(y)もできx表さ回最長サブ現れます。うまくプレフィックスツリーの腹からシフトする限り。

/* programed by white-55kai */
#if 0
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<utility>
#include<stdlib.h>
#include<time.h>
#else
#include<bits/stdc++.h>
#endif

#if 0
#include<cmath>
#endif

#define REP(i,l,r) for (reg int i=(l);i<=(r);++i)
#define RREP(i,r,l) for (int i=(r);i>=(l);--i)
#define rep(i,l,r) for (int i=(l);i<(r);++i)
#define rrep(i,r,l) for (int i=(r);i>(l);--i)
#define foredge(i,u) for (int i=la[u];i;i=ne[i])
#define mem(a) memset(a,0,sizeof(a))
#define memid(a) memset(a,63,sizeof(a))
#define memax(a) memset(a,127,sizeof(a))
#define dbg(x) cout<<#x<<" = "<<x<<endl
#define reg register
#define tpn typename
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;

template <tpn A> inline A Max(const A &x,const A &y){
    return x>y?x:y;
}
template <tpn A> inline A Min(const A &x,const A &y){
    return x<y?x:y;
}
template <tpn A> inline void Swap(A &x,A &y){
    x^=y,y^=x,x^=y;
}
template <tpn A> inline A Abs(const A &x){
    return x>0?x:-x;
}
#if 1
template <tpn A> inline void read(A &x){
    char c;
    A neg=1;
    do{
        c=getchar();
    }while ((c<'0'||c>'9')&&c!='-');
    if (c=='-') neg=-1,c=getchar();
    x=0;
    do{
        x=x*10+c-48;
        c=getchar();
    }while (c>='0'&&c<='9');
    x*=neg;
}
template <tpn A,tpn B> inline void read(A &a,B &b){
read(a),read(b);}
template <tpn A,tpn B,tpn C> inline void read(A &a,B &b,C &c){
read(a),read(b),read(c);}
template <tpn A,tpn B,tpn C,tpn D> inline void read(A &a,B &b,C &c,D &d){
read(a),read(b),read(c),read(d);}
template <tpn A> inline void put(const A &tmp){
    A x=tmp;
    if (x==0){
        putchar('0');
        return;
    }
    if (x<0) putchar('-'),x=-x;
    char buf[30];
    int buf_size=0;
    while (x){
        buf[++buf_size]=x%10+48;
        x/=10;
    }
    RREP(i,buf_size,1) putchar(buf[i]);
}
#else
namespace fastIO {
    #define buf_size 100000
    #define LL long long
    bool error;
    inline char gc() {
        static char buf[buf_size + 1], *l = buf, *r = buf;
        if (l == r) {
            l = buf;
            r = buf + fread(buf, 1, buf_size, stdin);
            if (l == r) {
                error = 1;
                return -1;
            }
        }
        return *l ++;
    }
    inline bool blank(char ch) {
        return ch == '\n' || ch == '\t' || ch == ' ' || ch == '\r' || error;
    }
    inline bool read_int(int &x) {
        char ch;
        int f = 1;
        while (blank(ch = gc()));
        if (error) return false;
        x = 0;
        if (ch == '-') f = -1, ch = gc();
        while (1) {
            x = (x << 1) + (x << 3) + ch - '0';
            if (!isdigit(ch = gc())) break;
        }
        x *= f;
        return true;
    }
    inline bool read_LL(LL &x) {
        char ch;
        LL f = 1;
        while (blank(ch = gc()));
        if (error) return false;
        x = 0;
        if (ch == '-') f = -1, ch = gc();
        while (1) {
            x = (x << 1) + (x << 3) + ch - '0';
            if (!isdigit(ch = gc())) break;
        }
        x *= f;
        return true;
    }
    inline bool read_char(char &x) {
        char ch;
        while (blank(ch = gc()));
        if (error) return false;
        x = ch;
        return true;
    }
    inline void put_int(int x) {
        if (!x) {
            putchar('0');
            return;
        }
        if (x < 0) {
            x = -x;
            putchar('-');
        }
        static int out[13];
        int len = 0;
        while (x) {
            out[++ len] = x % 10;
            x /= 10;
        }
        while (len) putchar(out[len --] + '0');
    }
    inline void put_LL(LL x) {
        if (!x) {
            putchar('0');
            return;
        }
        if (x < 0) {
            x = -x;
            putchar('-');
        }
        static LL out[20];
        int len = 0;
        while (x) {
            out[++ len] = x % 10;
            x /= 10;
        }
        while (len) putchar(out[len --] + '0');
    }
    #undef buf_size
    #undef LL
}
using namespace fastIO;
#endif
inline int mul_mod(int a,int b,int mo){
    int ret;
    __asm__ __volatile__ ("\tmull %%ebx\n\tdivl %%ecx\n":"=d"(ret):"a"(a),"b"(b),"c"(mo));
    return ret;
}
const int N = 200005;
int n;
char s[N];
namespace segmerge {
struct node {
    int l, r, lson, rson;
};
node tree[N * 80];
int cnt;
int newnode(int x, int l, int r) {
    ++cnt;
    tree[cnt].l = l, tree[cnt].r = r;
    if (l == r) return cnt;
    int mid = l + r >> 1, now = cnt;
    if (x <= mid) tree[now].lson = newnode(x, l, mid);
    else tree[now].rson = newnode(x, mid + 1, r);
    return now;
}
int merge(int lt, int rt) {
    if (!lt) return rt;
    if (!rt) return lt;
    int now = ++cnt;
    tree[now].l = tree[lt].l, tree[now].r = tree[lt].r;
    tree[now].lson = merge(tree[lt].lson, tree[rt].lson);
    tree[now].rson = merge(tree[lt].rson, tree[rt].rson);
    return now;
}
int query(int k, int l, int r) {
    if (!k) return 0;
    if (tree[k].l > r || tree[k].r < l) return 0;
    if (tree[k].l >= l && tree[k].r <= r) return 1;
    if (query(tree[k].lson, l, r)) return 1;
    else return query(tree[k].rson, l, r);
}
}
struct SAM {
    int len[N << 1], par[N << 1], ch[N << 1][26];
    int lp, p, np, q, nq, sz;
    SAM() { lp = sz = 1; }
    inline void insert(int x) {
        len[np = ++sz] = len[p = lp] + 1;
        par[lp = np] = 1;
        for (; p; p = par[p]) {
            if (!ch[p][x]) ch[p][x] = np;
            else {
                q = ch[p][x];
                if (len[q] == len[p] + 1) par[np] = q;
                else {
                    nq = ++sz;
                    len[nq] = len[p] + 1;
                    par[nq] = par[q];
                    par[q] = par[np] = nq;
                    memcpy(ch[nq], ch[q], sizeof(ch[q]));
                    for (; p && ch[p][x] == q; p = par[p]) ch[p][x] = nq;
                }
                return;
            }
        }
    }
    int root[N << 1], la[N << 1], ne[N << 1], en[N << 1], edgenum;
    int ans;
    int f[N << 1], pos[N << 1], r[N << 1];
    inline void add(int x, int y) {
        ne[++edgenum] = la[x];
        en[edgenum] = y;
        la[x] = edgenum;
    }
    void dfs(int u) {
        foredge(i, u) {
            dfs(en[i]);
            root[u] =segmerge::merge(root[u], root[en[i]]);
            r[u] = max(r[u], r[en[i]]);
        }
    }
    inline void build() {
        p = 1;
        REP(i, 1, n) {
            p = ch[p][s[i] - 'a'];
            r[p] = i;
            root[p] = segmerge::newnode(i, 1, n);
        }
        REP(i, 2, sz) add(par[i], i);
        dfs(1);
    }
    inline void get(int u) {
        if (u != 1) {
            if (par[u] == 1) f[pos[u] = u] = 1;
            else {
                int v = pos[par[u]];
                if (segmerge::query(root[v], r[u] - len[u] + len[v], r[u] - 1))
                    f[pos[u] = u] = f[v] + 1;
                else f[u] = f[par[u]], pos[u] = v;
            }
            ans = max(ans, f[u]);
        }
        foredge(i, u) get(en[i]);
    }
    inline void getans() { get(1); }
};
SAM van;
int main() {
    // read(n);
    scanf("%s", s + 1);
    n = strlen(s + 1);
    REP(i, 1, n) van.insert(s[i] - 'a');
    van.build();
    van.getans();
    cout << van.ans << endl;
    return 0;
}

セグメントツリーは何を維持合併(存続可能)の国境に関連する多くの問題があるが使用されているようです\(右\)まだ設定してください。

SAM + LCT

私はこれらの事をもっと魔法を聞きました。

LCTは動的な維持を検討しparent、各ノードのツリー\(右\)が最大値を集中し、この値が呼び出されます\(val_uの\) 新しい抗接頭辞を追加するとき、LCTが折半変更されることがわかった\(\ \ログ)重い息子を、接尾辞木を変更することです\(\ログ\)段落\(ヴァル\)を、場合には、この時間を求めています情報および\(ヴァル\)に関連する、それを維持することができます。

T3

二つの\(\ \ログ)前処理(\ \ログ\)回答\(S [L、R] \) サブストリングの本質的に異なる数にします。

以下のようなセグメントツリーの外観をマージする少なくできます。

上記のトリック、オフライン走査線が解決することを考えてみましょう。便宜上、プラス前方の文字から、され構築されている\(S [lは、N] \) 接尾辞木の、各文字列は、プライマリサフィックスに挿入され、メンテナンスセグメントツリーと、現在のポイント\(Iは\)左端点は、\(X \で、[I、N-] \)の回答の右端です。

かかわらず、現在のセグメントの\(val_x \) サブストリングの長さ間隔\([MINLEN、mxlen] \) 明らかに、特定の長さのインターバルの長さ\(LEN \)は、寄与は、セグメントツリーである\([iがLEN、val_x + \ + LEN))をこの間隔プラス一方。いくつかの文字列の事は木で、その後、同様の平行四辺形、XJBメンテナンスで構成される行を追加しますように見えます。

コード:タイトルが出て来なかった、データがどのようなコードを書いていませんか?

T4

二つの\(\ \ログ)前処理(\ \ログ\)回答\(S [L、R] \) ノードの接尾辞木です。

(♂良いああ、不滅のタイトル)

それでも走査線セグメントツリーを、上記と同じプラスメンテナンス情報を検討しました。

最初にやる、そして求めている、となり2差を考えてみましょう\(\ I FORALL、J \ GEQ I \)で、\([:J] ST \ ) を挿入(S [I、Jする] \ \) 追加されますノードの数。

新しい葉:新しいノードを考えてみましょう、それは明らかに2つのタイプに分けることが必要があります\(S [I:J] \) および新しいnqノード。直接添加前者(N-I + 1 \ \ ) キーである、ライン。

\(last_u、diff_u \)ルートにそれぞれ\(U \)この時間をサブストリング、リアルタイム左息子列の切り替えポイントに対応します。

LCTに沿ってジャンプアップを考慮すると、明らかにポイントを切り替えるの唯一の本当の息子は答え寄与を生成します。具体的には、中右点である\([last_u + len_u、diff_u + LEN + _U)\) クエリであるプラス1。その後、元の文字列のサフィックスオートマトンの新機能の詳細があります\(NQの\は)ノード、そこにあるべきlast[nq]=last[q],diff[nq]=0ではなく、継承よりも、q

ここでは、古典的なモデル位置しています。ので、しかし考えると、いくつか間違ってあります\(uは\)です\(左\) 明らかでいくつかがあるかもしれない濃縮物((last_u、diff_u)\ \を ) 間のポイントこれらの次の文字列ですが、\ (S [last_u + len_u] \)が、これらのために、\(左\)ので、ルートからチャレンジ質問右点としてセットポイントに\(U \)接尾文字列の両方のノードにだけでなく、二人の子供を持っていますノードは、そのため多くを支払います。

これらの問題に対処する方法を考えてみましょう。各お問い合わせは、サブサフィックスはそれが重複する可能性がありのみを表します。長さであるため、場合及び(D \)を\セクションが繰り返さサフィックスを引き起こす可能性があり、その後の長さ\(C <D \)間隔サフィックスは明らかに重複すること。接尾辞木の間隔で二つ以上の人の息子が存在する場合と場合にのみ、範囲のサフィックスは、重複が発生することができます。そして、レンジサフィックスが数回反復を引き起こす可能性を検討し、その答えは明らかです。問題を解決するよう+セグメントは、次に、各クエリに対して(他に、反対側のサフィックスオートマトン)バイナリツリーマージすることができる(二が\(\ \ログ) A)。

それでは、どのように最適化する(\を記録\)\を、それを?GUGU区。

拡大問題:すべてのサブ文字列と接尾辞木のノードの文字列をお願いします。

変更せずに前のセクション。

続く質問を作成する方法を考えてみましょう。(からの統計\(最後の\)に直接?Aのように失うことにどのように多くの文字列の上に\(\)\ログ

おすすめ

転載: www.cnblogs.com/pupuvovovovo/p/11704956.html