Loj2059. 「TJOI / HEOI2016」字符串

来源:互联网 发布:les聊天软件 编辑:程序博客网 时间:2024/06/05 19:51

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;}
原创粉丝点击