后缀自动机学习笔记

诸神眷顾的幻想乡

给定一个叶节点不超过20的无根树,每个节点有一个字母。问树上路径形成的本质不同的字符串的个数。

广义后缀自动机裸题。从每个叶节点做bfs,记录父亲的状态从而插入建立后缀自动机。我们知道一个后缀自动机本质不同的子串个数为 imax[i]min[i]+1=max[i]max[fa[i]] ,或者DAG上dp即可。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100001*20, S = 10;
struct SAM {
    int chl[MAXN][S], fa[MAXN], maxl[MAXN];
    int top, root, last;
    void init()
    {
        top = root = last = 1;
        memset(chl, 0, sizeof chl);
        memset(fa, 0, sizeof fa);
        memset(maxl, 0, sizeof maxl);
    }
    void push(int stat, int x)
    {
        int p = stat, np = ++top; maxl[np] = maxl[p] + 1;
        while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
        if (!p) fa[np] = root;
        else {
            int q = chl[p][x];
            if (maxl[q] == maxl[p] + 1) fa[np] = q;
            else {
                int nq = ++top; maxl[nq] = maxl[p] + 1;
                memcpy(chl[nq], chl[q], sizeof chl[q]);
                fa[nq] = fa[q], fa[q] = fa[np] = nq;
                while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
            }
        }
        last = np;
    }
}sam;

queue<int> que;
int stat[102400], rd[102400], col[102400];

struct node {
    int to, next;
} edge[202400];
int head[102400], top = 0;
void push(int i, int j)
{ rd[i]++, ++top, edge[top] = (node) {j, head[i]}, head[i] = top; }

void bfs(int nd)
{
    //printf("BFS : %d\n", nd);
    memset(stat, 0, sizeof stat);
    stat[nd] = 1;
    que.push(nd);
    while (!que.empty()) {
        int tp = que.front(); que.pop();
        sam.push(stat[tp], col[tp]);
        //printf("%d -- %d--+%d-->%d\n", tp, stat[tp], col[tp], sam.last);
        for (int i = head[tp]; i; i = edge[i].next)
            if (!stat[edge[i].to])
                stat[edge[i].to] = sam.last, que.push(edge[i].to);
    }
}

int n, c;

void solve()
{
    sam.init();
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; i++)
        scanf("%d", &col[i]);
    for (int i = 1; i < n; i++) {
        int u, v; scanf("%d%d", &u, &v);
        push(u, v); push(v, u);
    }
    for (int i = 1; i <= n; i++)
        if (rd[i] == 1)
            bfs(i);
    long long ans = 0;
    for (int i = 2; i <= sam.top; i++)
        ans += sam.maxl[i] - sam.maxl[sam.fa[i]];
    printf("%lld", ans);
}

int main()
{
    //freopen("zjoi15_substring.in", "r", stdin);
    //freopen("zjoi15_substring.out", "w", stdout);
    solve();
    return 0;
}

POI2000 公共串

给你 n5 个长度不超过2000的字符串,求最长公共子串。

SAM解法:只要在匹配的时候记录每个节点对于第i个串匹配的最长距离,然后xjb取max和min就好了。

APIO2014 回文串

给定一个字符串 S ,求一个回文子串T,最大化 |T|times(T) times(T) 为出现次数。

首先我们用manacher算法求出本质不同的回文串。由于manacher的复杂度为 O(n) ,回文串个数为 O(n) 。我们一个个询问其出现次数即可。现在问题转化为了对于一个子串,要在 O(lgn) 的时间内求出其出现次数。

首先我们预处理出 pos[r]:=S[1..r] 在后缀自动机中的位置,那么 S[l,r] 就是parent树上最后一个maxl大于l-r+1的节点。用倍增处理即可。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 300005*2, S = 26;

int pos[MAXN]; // S[1..r]对应状态
int fa[MAXN][21];
char str[MAXN];
int right_siz[MAXN];
int stk[MAXN], top = 0, rd[MAXN];

struct SAM {
    int chl[MAXN][S], fa[MAXN], maxl[MAXN];
    int top, root, last;
    void clear()
    {
        top = root = last = 1;
        memset(chl, 0, sizeof chl), memset(fa, 0, sizeof fa), memset(maxl, 0, sizeof maxl);
    }
    SAM() { clear(); }
    void push(int x)
    {
        int p = last, np = ++top; maxl[np] = maxl[p] + 1, right_siz[np]++;
        while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
        if (!p) fa[np] = root;
        else {
            int q = chl[p][x]; 
            if (maxl[q] == maxl[p] + 1) fa[np] = q;
            else {
                int nq = ++top; maxl[nq] = maxl[p]+1;
                memcpy(chl[nq], chl[q], sizeof chl[q]);
                fa[nq] = fa[q], fa[q] = fa[np] = nq;
                while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
            }
        }
        last = np;
    }
} sam;

void top_sort()
{
    for (int i = 1; i <= sam.top; i++) rd[sam.fa[i]]++;
    for (int i = 1; i <= sam.top; i++) if (rd[i] == 0) stk[++top] = i;
    while (top) {
        int t = stk[top--];  rd[sam.fa[t]]--, right_siz[sam.fa[t]] += right_siz[t];
        if (rd[sam.fa[t]] == 0) stk[++top] = sam.fa[t];
    }
}

void init()
{
    scanf("%s", str+1);
    for (char *p = str+1; *p != '\0'; ++p)
        sam.push(*p-'a');
    int len = strlen(str+1);
    for (int i = 1, nd = sam.root; i <= len; i++) {
        nd = sam.chl[nd][str[i]-'a'];
        pos[i] = nd;
    }
    for (int i = 1; i <= sam.top; i++) fa[i][0] = sam.fa[i];
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i <= sam.top; i++)
            fa[i][j] = fa[fa[i][j-1]][j-1];
    top_sort(); // Count right_siz
} 

long long ans = 0;
void query(int i, int j)
{
    int nd = pos[j];
    for (int k = 20; k >= 0; k--)
        if (sam.maxl[fa[nd][k]] >= j-i+1)
            nd = fa[nd][k];
    ans = max(ans, (long long)(j-i+1)*right_siz[nd]);
}

int p[MAXN];

void work()
{
    int len = strlen(str+1);
    int id = 0, mx = 0; // manacher
    str[0] = '$'; 
    for (int i = 1; i <= len; i++) {
        if (mx > i) p[i] = min(p[id-(i-id)], mx-i); else p[i] = 1, query(i, i);
        while (str[i-p[i]] == str[i+p[i]]) query(i-p[i], i+p[i]), p[i]++;
        if (i+p[i] > mx) id = i, mx = i+p[i]; 
    }
    id = mx = 0;
    for (int i = 1; i <= len; i++) {
        if (mx > i) p[i] = min(p[id-(i-id)], mx-i); else p[i] = 0;
        while (str[i-p[i]] == str[i+p[i]+1]) query(i-p[i], i+p[i]+1), p[i]++;
        if (i+p[i] > mx) id = i, mx = i+p[i];
    }
    cout << ans << endl;
}

int main()
{
    init();
    work();
    return 0;
}

HAOI2016找相同字符

给定两个串S1,S2,统计他们的公共子串总数。两个子串不同,当且仅当长度不同或出现位置不同。

SA解法:将S1和S2用一个’#’隔开,求出height数组,由于公共子串是后缀的前缀,因此答案就是所有前一半的后缀和后一半的后缀的lcp的和。用单调栈扫两遍记录答案即可。最优复杂度 O(n)

SAM解法:这个做法比较鬼畜。先把第一个串建立后缀自动机,再把第二个串在上面跑。到达一个状态x时匹配长度为len对答案的贡献分为两部分:

  1. 当前位置的收获 (lenmin(x)+1)×|Right(x)|
  2. 由于匹配了当前位置,自然匹配了父亲节点,就有 ix(max(i)min(i)+1)×|Right(i)|

第一部分为 O(1) ,第二部分可以拓扑排序后 O(n) 预处理。总复杂度为 O(n)

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005*2, S = 26;
int right_siz[MAXN];

struct SAM {
    int chl[MAXN][S], fa[MAXN], maxl[MAXN];
    int top, root, last;
    void clear() 
    { top = root = last = 1; }
    SAM()
    { clear(); }
    void push(int x)
    {
        int p = last, np = ++top; maxl[np] = maxl[p] + 1; right_siz[np]++;
        while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
        if (!p) fa[np] = root;
        else {
            int q = chl[p][x];
            if (maxl[q] == maxl[p] + 1) fa[np] = q;
            else {
                int nq = ++top; maxl[nq] = maxl[p] + 1;
                memcpy(chl[nq], chl[q], sizeof chl[q]);
                fa[nq] = fa[q], fa[q] = fa[np] = nq;
                while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
            }
        }
        last = np;
    }
} sam;

char s1[MAXN], s2[MAXN];

int stk[MAXN], top = 0;
int rd[MAXN];
int topo[MAXN], tp_top = 0;
int canc[MAXN];
int vis[MAXN];

void dfs(int nd, string str) 
{
    printf("Id = %d, pre = %d, dis = %d, right = %d, cacc = %d\n", nd, sam.fa[nd], sam.maxl[nd], right_siz[nd], canc[nd]);
    vis[nd] = 1;
    for (int i = 0; i < S; i++)
        if (sam.chl[nd][i])
            printf("-+%c-> %d\n", i+'a', sam.chl[nd][i]);
    for (int i = 0; i < S; i++)
        if (sam.chl[nd][i] && !vis[sam.chl[nd][i]])
            dfs(sam.chl[nd][i], str+char(i+'a'));
}

void top_sort()
{
    for (int i = 1; i <= sam.top; i++) rd[sam.fa[i]]++;
    for (int i = 1; i <= sam.top; i++) if (rd[i] == 0) stk[++top] = i;
    while (top) {
        int t = stk[top--]; topo[++tp_top] = t, rd[sam.fa[t]]--;
        if (rd[sam.fa[t]] == 0) stk[++top] = sam.fa[t];
    }
    for (int i = 1; i <= tp_top; i++) right_siz[sam.fa[topo[i]]] += right_siz[topo[i]];
    for (int i = tp_top; i >= 1; i--) 
        if (topo[i] != sam.root && sam.fa[topo[i]] != sam.root) 
            canc[topo[i]] = canc[sam.fa[topo[i]]] + (sam.maxl[sam.fa[topo[i]]]-sam.maxl[sam.fa[sam.fa[topo[i]]]])*right_siz[sam.fa[topo[i]]];
}

void work()
{
    scanf("%s%s", s1, s2);
    for (char *p = s1; *p != '\0'; p++) sam.push(*p-'a');
    top_sort();
    int nd = sam.root, len = 0;
    long long ans = 0;
    //int l = strlen(s2); s2[l] = '$', s2[l+1] = '\0';
    for (char *p = s2; *p != '\0'; p++) {
        if (sam.chl[nd][*p-'a']) nd = sam.chl[nd][*p-'a'], len++;
        else {
            while (nd && !sam.chl[nd][*p-'a']) nd = sam.fa[nd];
            if (!nd) nd = sam.root, len = 0;
            else len = sam.maxl[nd]+1, nd = sam.chl[nd][*p-'a'];
        }
        ans += canc[nd] + (len-sam.maxl[sam.fa[nd]])*right_siz[nd];
        //cout << ans << endl;
    }
    cout << ans << endl;
}

int main()
{
    work();
    return 0;
}

2555: SubString

LCT维护Right数组大小…

神题。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1600002, S = 26;

struct LCT {
    int chl[MAXN][2], fa[MAXN], siz[MAXN], flag[MAXN], rev[MAXN], add[MAXN];
    int stk[MAXN];
    int root, top;
    void clear()
    { root = top = 0; }
    LCT()
    { clear(); }
    bool isrt(int nd)
    { return chl[fa[nd]][0] != nd && chl[fa[nd]][1] != nd; }
    void pdw(int nd)
    {
        int &lc = chl[nd][0], &rc = chl[nd][1];
        if (lc) rev[lc] ^= rev[nd], add[lc] += add[nd];
        if (rc) rev[rc] ^= rev[nd], add[rc] += add[nd];
        if (rev[nd]) rev[nd] = 0, swap(lc, rc);
        if (add[nd]) siz[nd] += add[nd], add[nd] = 0;
    }
    void zig(int nd)
    {
        int p = fa[nd], g = fa[p];
        int tp = chl[p][0] != nd, tg = chl[g][0] != p, son = chl[nd][tp^1];
        if (!isrt(p)) chl[g][tg] = nd;
        chl[nd][tp^1] = p, chl[p][tp] = son;
        fa[nd] = g, fa[p] = nd, fa[son] = p;
    }
    void splay(int nd)
    {
        int top = 0; stk[++top] = nd;
        for (int x = nd; !isrt(x); x = fa[x])
            stk[++top] = fa[x];
        while (top) pdw(stk[top--]);
        while (!isrt(nd)) {
            int p = fa[nd], g = fa[p];
            int tp = chl[p][0] != nd, tg = chl[g][0] != p;
            if (isrt(p)) { zig(nd); break; }
            else if (tp == tg) zig(p), zig(nd);
            else zig(nd), zig(nd);
        }
    }
    void dfs(int nd, int tab)
    {
        if (!nd) return;
        for (int i = 1; i <= tab; i++) putchar(' ');
        printf("nd = %d, flag = %d, siz = %d, lc = %d, rc = %d, fa = %d, rev = %d\n", nd, flag[nd], siz[nd], chl[nd][0], chl[nd][1], fa[nd], rev[nd]);
        dfs(chl[nd][0], tab+2);
        dfs(chl[nd][1], tab+2);
    }
    void access(int x)
    {
        for (int y = 0; x; x = fa[y = x])
            splay(x), chl[x][1] = y;
    }
    void mkt(int x)
    { access(x), splay(x), rev[x] ^= 1; }
    void link(int x, int y)
    { mkt(x); splay(x); fa[x] = y; }
    void cut(int x, int y)
    { mkt(x), access(y), splay(y), fa[x] = chl[y][0] = 0;}
    void lct_link(int x, int y) // x->y
    {
        //printf("LINK : %d-->%d\n", x, y);
        link(x, y), mkt(1);
        //puts("---");
        access(y), splay(y), siz[y] += siz[x];
        //puts("---");
        if (chl[y][0]) add[chl[y][0]] += siz[x];
    }
    void lct_cut(int x, int y) // cut x->y
    {
        cut(x, y), mkt(1);
        access(y), splay(y), siz[y] -= siz[x];
        if (chl[y][0]) add[chl[y][0]] -= siz[x];
    }
    void set_flag(int x)
    { mkt(x), splay(x), siz[x] = 1; }
    int find_fa(int x)
    {
        access(x);
        while (!isrt(x)) x = fa[x];
        return x;
    }
    int query(int nd)
    {
        mkt(nd), splay(nd);
        return siz[nd];
    }
} lct;

struct SAM {
    int chl[MAXN*2][S], fa[MAXN*2], maxl[MAXN*2];
    int top, last, root;
    void clear()
    { top = last = root = 1; }
    SAM()
    { clear(); }
    void push(int x)
    {
        //cout << "PUSH : " << (char)(x+'a') << endl;
        int p = last, np = ++top; maxl[np] = maxl[p] + 1; lct.set_flag(np);
        //puts("j");
        while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
        //puts("jj");
        if (!p) fa[np] = root, lct.lct_link(np, root);
        else {
            int q = chl[p][x];
            if (maxl[q] == maxl[p] + 1) fa[np] = q, lct.lct_link(np, q);
            else {
                int nq = ++top; maxl[nq] = maxl[p] + 1;
                memcpy(chl[nq], chl[q], sizeof chl[q]);
                lct.lct_link(nq, fa[q]), fa[nq] = fa[q];
                lct.lct_cut(q, fa[q]), lct.lct_link(q, nq), fa[q] = nq;
                lct.lct_link(np, nq), fa[np] = nq;
                while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
            }
        }
        //puts("jjj");
        last = np;
    }
} sam;

char str[MAXN*2];
int q, mask = 0;

void decode(int mask)
{
    int len = strlen(str);
    for (int j = 0; j < len; j++) {
        mask = (mask*131+j)%len;
        swap(str[j], str[mask]);
    }
}

void get_str(char str[])
{
    scanf("%s", str);
    decode(mask);
}

char opt[10];
int main()
{
    //freopen("substring.in", "r", stdin);
    //freopen("substring.out","w",stdout);
    scanf("%d", &q);
    scanf("%s", str);
    //puts("hah");
    for (char *p = str; *p != '\0'; p++)
        sam.push(*p-'A');
    //puts("hah");
    for (int i = 1; i <= q; i++) {
        scanf("%s", opt);
        //cout << opt << endl;
        if (opt[0] == 'A') {
            get_str(str);
            for (char *p = str; *p != '\0'; p++)
                sam.push(*p-'A');
        } else {
            get_str(str);
            int nd = sam.root, flag = 0;
            for (char *p = str; *p != '\0'; p++) {
                if (!sam.chl[nd][*p-'A']) {flag = 1; break; }
                else nd = sam.chl[nd][*p-'A'];
            }
            if (flag) puts("0");
            else {
                int ans = lct.query(nd);
                printf("%d\n", ans);
                mask ^= ans;
            }
        }
    }
    return 0;
}

poj2774: Long Long Message

裸题,SAM直接碾。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAXN = 200005;
int chl[MAXN][26], fa[MAXN], maxl[MAXN];
int top = 1, root = 1, last = 1;
void push(int x)
{
    int p = last, np = ++top; maxl[np] = maxl[p]+1;
    while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
    if (!p) fa[np] = root;
    else {
        int q = chl[p][x];
        if (maxl[q] == maxl[p]+1) fa[np] = q;
        else {
            int nq = ++top; maxl[nq] = maxl[p]+1;
            memcpy(chl[nq], chl[q], sizeof chl[q]);
            fa[nq] = fa[q], fa[q] = fa[np] = nq;
            while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
        }
    }
    last = np;
}

char str[MAXN];
int main()
{
    scanf("%s", str);
    for (char *p = str; *p != '\0'; ++p)
        push(*p-'a');
    scanf("%s", str);
    int nd = root, len = 0, ans = 0;
    for (char *p = str; *p != '\0'; ++p) {
        int x = *p-'a';
        if (chl[nd][x]) nd = chl[nd][x], len++;
        else {
            while (nd && !chl[nd][x]) nd = fa[nd];
            if (!nd) nd = root, len = 0;
            else len = maxl[nd]+1, nd = chl[nd][x];
        }
        ans = max(ans, len);
    }
    cout << ans << endl;
    return 0;
}

[SPOJ705]不同的子串

模板复习计划,裸题。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 50005*2;
struct SAM {
    int chl[MAXN][26], fa[MAXN], maxl[MAXN];
    int top, root, last;
    void clear()
    { top = root = last = 1, memset(chl, 0, sizeof chl),
    memset(fa, 0, sizeof fa), memset(maxl, 0, sizeof maxl); }
    SAM() { clear(); }
    void push(int stat, int x)
    {
        int p = last, np = ++top; maxl[np] = maxl[p]+1;
        while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
        if (!p) fa[np] = root;
        else {
            int q = chl[p][x];
            if (maxl[q] == maxl[p]+1) fa[np] = q;
            else {
                int nq = ++top; maxl[nq] = maxl[p]+1;
                memcpy(chl[nq], chl[q], sizeof chl[q]);
                fa[nq] = fa[q], fa[q] = nq, fa[np] = nq;
                while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
            }
        }
        last = np;
    }
} sam;

char str[MAXN];

int main()
{
    freopen("subst1.in", "r", stdin);
    freopen("subst1.out", "w", stdout);
    scanf("%s", str+1);
    for (char *p = str+1; *p != '\0'; ++p)
        sam.push(sam.last, *p-'A');
    long long ans = 0;
    for (int i = 1; i <= sam.top; i++)
        ans += sam.maxl[i]-sam.maxl[sam.fa[i]];
    cout << ans << endl;
    return 0;
}

bzoj2806: [Ctsc2012]Cheat

比较神的题…
后缀自动机上dp,由于dp决策有区间性质,可以用线段树或者单调队列维护。线段树版本 (O(nlg2n)) 会TLE,明天写单调队列的。

线段树(TLE):

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1100001*2;
int tree[MAXN][2], fin[MAXN], col[MAXN], trie_root = 0, trie_top = 0;
void push_str(int &nd, const char *str)
{
    if (!nd) nd = ++trie_top;
    if (*str == '\0') fin[nd] = 1;
    else push_str(tree[nd][*str-'0'], str+1), col[tree[nd][*str-'0']] = *str-'0';
}

int chl[MAXN][2], fa[MAXN], maxl[MAXN], root = 1, top = 1;
void push(int p, int x, int &last)
{
    int np = ++top; maxl[np] = maxl[p]+1;
    while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
    if (!p) fa[np] = root;
    else {
        int q = chl[p][x];
        if (maxl[q] == maxl[p]+1) fa[np] = q;
        else {
            int nq = ++top; maxl[nq] = maxl[p]+1;
            chl[nq][0] = chl[q][0], chl[nq][1] = chl[q][1];
            fa[nq] = fa[q], fa[q] = fa[np] = nq;
            while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
        }
    }
    last = np;
}

queue<int> que;
int stat[MAXN];

void build_tree()
{
    que.push(trie_root), stat[trie_root] = 1;
    while (!que.empty()) {
        int tp = que.front(); que.pop();
        int last;
        push(stat[tp], col[tp], last);
        for (int i = 0; i <= 1; i++)
            if (tree[tp][i])
                stat[tree[tp][i]] = last, que.push(tree[tp][i]);
    }
}

bool match(char str[], int l, int r)
{
    if (l <= 0 || l > r) return 0;
    int nd = root, ans = 0, len = 0;
    for (int i = l; i <= r; i++) {
        int x = str[i]-'0';
        if (chl[nd][x]) nd = chl[nd][x], len++;
        else {
            while (nd && !chl[nd][x]) nd = fa[nd];
            if (!nd) nd = root, len = 0;
            else len = maxl[nd]+1, nd = chl[nd][x];
        }
        ans = max(ans, len);
    } 
    return ans == r-l+1;
}

char str[MAXN];
int dp[MAXN], max_back[MAXN];
int n, m;

int zkw[(1<<21)+1], N = 1<<20;
void modify(int nd, int val)
{
    nd += N-1;
    zkw[nd] = val;
    for (int i = nd>>1; i; i >>= 1)
        zkw[i] = max(zkw[i*2], zkw[i*2+1]);
}
int ask_max(int l, int r)
{
    if (l > r) return 0;
    int ans = -233333333;
    for (l += N-1, r += N-1; l < r; l>>=1, r>>=1) {
        if (l&1) ans = max(ans, zkw[l++]);
        if (!(r&1)) ans = max(ans, zkw[r--]);
    }
    if (l == r) ans = max(ans, zkw[l]);
    return ans;
}

bool do_dp(int L)
{
    int l = strlen(str+1);
    memset(dp, 0, sizeof dp);
    memset(zkw, -127/3, sizeof zkw);
    int max_val = 0;
    modify(0, 0);
    for (int i = 1; i <= l; i++) {
        dp[i] = max_val;
        if (i-L >= 0 && max_back[i] <= i-L)
            dp[i] = max(dp[i], i+ask_max(max_back[i], i-L));
        max_val = max(max_val, dp[i]);
        modify(i, dp[i]-i);
    }
    return dp[l]*10 >= l*9;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        scanf("%s", str);
        push_str(trie_root, str);
    }
    build_tree();
    for (int i = 1; i <= n; i++) {
        scanf("%s", str+1);
        memset(max_back, 0, sizeof max_back);
        for (int i = 1, l = strlen(str+1); i <= l; i++) {
            int lf = 1, rt = i, mid;
            while (lf <= rt) {
                mid = (lf+rt)>>1;
                if (match(str, i-mid+1, i)) lf = mid+1;
                else rt = mid-1;
            }
            max_back[i] = i-(lf-1);
        } 
        int l = 1, r = strlen(str+1), mid;
        while (l <= r) {
            mid = (l+r)>>1;
            if (do_dp(mid)) l = mid+1;
            else r = mid-1;
        }
        printf("%d\n", l-1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值