Loj2059. 「TJOI / HEOI2016」字符串

Loj2059. 「TJOI / HEOI2016」字符串


題意:給定一個串,問S[a..b] 的子串與S[c..d] 的最長公共前綴的長度。

考慮將串反轉,則變成了最長公共後綴問題。

二分答案mid ,則轉化爲問S[dmid+1..d] 是不是S 的子串。

考慮用線段樹合併維護right 集合,在倍增找到串S[dmid+1,d] 的位置後,看其right 集合中是不是有[a+mid1,b] 的位置即可。

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

const int MAXN = 200005;

int lc[MAXN*50], rc[MAXN*50], top = 0;

int build(int &nd, int pos, int L, int R)
{
    nd = ++top;
    if (L == R) return nd;
    else {
        int mid = (L+R)>>1;
        if (pos <= mid) return build(lc[nd], pos, L, mid);
        else return build(rc[nd], pos, mid+1, R);
    }
}

int merge(int a, int b)
{
    if (a == 0 || b == 0) return a+b;
    int c = ++top;
    lc[c] = merge(lc[a], lc[b]), rc[c] = merge(rc[a], rc[b]);
    return c;
}

bool query(int nd, int opl, int opr, int L, int R)
{
    if (!nd) return 0;
    if (opl == L && opr == R) return 1;
    else {
        int mid = (L+R)>>1;
        if (opr <= mid) return query(lc[nd], opl, opr, L, mid);
        else if (opl > mid) return query(rc[nd], opl, opr, mid+1, R);
        else return query(lc[nd], opl, mid, L, mid)||query(rc[nd], mid+1, opr, mid+1, R);
    }
}

int chl[MAXN*2][26], fa[MAXN*2][21], maxl[MAXN*2], sam_top = 1, root = 1, last = 1;
int rt[MAXN*2], fin[MAXN*2];
int n, m;
char str[MAXN*2];

void push(int x, int id)
{
    int p = last, np = ++sam_top; maxl[np] = maxl[p]+1, build(rt[np], id, 1, n);
    while (p && !chl[p][x]) chl[p][x] = np, p = fa[p][0];
    if (!p) fa[np][0] = root;
    else {
        int q = chl[p][x];
        if (maxl[q] == maxl[p]+1) fa[np][0] = q;
        else {
            int nq = ++sam_top; maxl[nq] = maxl[p]+1;
            memcpy(chl[nq], chl[q], sizeof chl[q]);
            fa[nq][0] = fa[q][0], fa[q][0] = fa[np][0] = nq;
            while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p][0];
        }
    }
    fin[id] = last = np;
}

struct node {
    int to, next;
} edge[MAXN*2];
int head[MAXN*2], tree_top = 0;
void push_tree(int i, int j)
{ edge[++tree_top] = (node) {j, head[i]}, head[i] = tree_top; }

void dfs(int nd)
{
    // for (int i = 1; i <= tab; i++) cerr << " ";
    // cerr << nd << endl;
    for (int i = head[nd]; i; i = edge[i].next) {
        int to = edge[i].to;
        dfs(to), rt[nd] = merge(rt[nd], rt[to]);
    }
}

void init()
{
    scanf("%d%d", &n, &m);
    scanf("%s", str+1);
    reverse(str+1, str+n+1);
    for (int i = 1; i <= n; i++)
        push(str[i]-'a', 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];
    for (int i = 2; i <= sam_top; i++) {
        push_tree(fa[i][0], i);
        // cerr << i << "," << fa[i][0] << endl;
    }
    dfs(root);
}

bool judge(int pos, int mid, int a, int b)
{
    for (int i = 19; i >= 0; i--)
        if (maxl[fa[pos][i]] >= mid)
            pos = fa[pos][i];
    /*cerr << pos << " " << rt[pos] << " " << a-mid+1 << " " << b << endl;
    for (int i = 1; i <= 5; i++)
        cerr << query(rt[pos], i, i, 1, n) << " ";
        cerr << endl;*/
    return query(rt[pos], a+mid-1, b, 1, n);
}

int main()
{
    init();
    for (int i = 1; i <= m; i++) {
        int a, b, c, d;
        scanf("%d%d%d%d", &b, &a, &d, &c);
        a = n-a+1, b = n-b+1, c = n-c+1, d = n-d+1;
        int pos = fin[d];
        int L = 1, R = min(b-a+1, d-c+1), mid;
        while (L <= R) {
            mid = (L+R)>>1;
            if (judge(pos, mid, a, b)) L = mid+1;
            else R = mid-1;
        }
        printf("%d\n", L-1);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章