bzoj4556

后缀自动机+二分+倍增+线段树合并

后缀自动机真好用

后面一个串是固定的,那么我们要对前面的串进行一些操作。我们想既然是求lcp,那么我们得先翻转原串,这样前缀变成了后缀,然后二分一下,从d在自动机上的位置向上倍增,走到第一个Max大于当前答案的位置,用线段树合并判断一下当前是否满足。还是很好写的,具体看代码。注意线段树合并必须新开一个节点。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, m;
int fa[N][19], mp[N << 1];
vector<int> G[N];
namespace Segment_Tree
{
    int cnt;
    int root[N], sum[N * 25], lc[N * 25], rc[N * 25];
    void update(int &x, int l, int r, int p, int d)
    {
        x = ++cnt;
        sum[x] += d;
        if(l == r) return;
        int mid = (l + r) >> 1;
        if(p <= mid) update(lc[x], l, mid, p, d);
        else update(rc[x], mid + 1, r, p, d);
    }
    int merge(int u, int v)
    {
        if(!u) return v;
        if(!v) return u;
        int w = ++cnt;
        sum[w] = sum[u] + sum[v];
        lc[w] = merge(lc[u], lc[v]);
        rc[w] = merge(rc[u], rc[v]);
        return w;
    }
    int query(int x, int l, int r, int a, int b)
    {
        if(!x || l > b || r < a) return 0;
        if(l >= a && r <= b) return sum[x];
        int mid = (l + r) >> 1;
        return (query(lc[x], l, mid, a, b) + query(rc[x], mid + 1, r, a, b)); 
    }
} using namespace Segment_Tree;
namespace SAM 
{
    struct node {
        int val, par;
        int ch[26];
    } t[N];
    int sz = 1, Root = 1, last = 1;
    int nw(int x)
    {
        t[++sz].val = x;
        return sz;
    }
    void extend(int c)
    {
        int p = last, np = nw(t[p].val + 1);
        while(p && !t[p].ch[c]) t[p].ch[c] = np, p = t[p].par;
        if(!p) t[np].par = Root;
        else
        {
            int q = t[p].ch[c];
            if(t[q].val == t[p].val + 1) t[np].par = q;
            else
            {
                int nq = nw(t[p].val + 1);
                memcpy(t[nq].ch, t[q].ch, sizeof(t[q].ch));
                t[nq].par = t[q].par;
                t[q].par = t[np].par = nq;
                while(p && t[p].ch[c] == q) t[p].ch[c] = nq, p = t[p].par;
            }
        }
        last = np;
        mp[t[np].val] = np;
        update(root[np], 1, n, t[np].val, 1);
    }
    void dfs(int u)
    {
        for(int i = 0; i < G[u].size(); ++i) 
        {
            int v = G[u][i];
            fa[v][0] = u;
            dfs(v);
            root[u] = merge(root[u], root[v]);
        }
    }
} using namespace SAM;
char s[N];
bool check(int len, int l, int r, int u)
{   
    for(int i = 18; i >= 0; --i) 
        if(t[fa[u][i]].val >= len) 
            u = fa[u][i];
//  if(t[u].val < len) return 0;
    return query(root[u], 1, n, l + len - 1, r);
}
int main()
{
//  freopen("heoi2016_str.in", "r", stdin);
//  freopen("heoi2016_str.out", "w", stdout);
    scanf("%d%d%s", &n, &m, s + 1);
    reverse(s + 1, s + n + 1);
    for(int i = 1; i <= n; ++i) extend(s[i] - 'a');
    for(int i = 2; i <= sz; ++i) G[t[i].par].push_back(i);
    dfs(Root); 
    for(int j = 1; j <= 18; ++j)
        for(int i = 1; i <= sz; ++i) 
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
    while(m--)
    {
        int a, b, c, d, ta, tb, tc, td;
        scanf("%d%d%d%d", &ta, &tb, &tc, &td);
        a = n - tb + 1;
        b = n - ta + 1;
        c = n - td + 1;
        d = n - tc + 1;
        int l = 0, r = min(b - a + 2, d - c + 2), ans = 0;
        while(r - l > 1)
        {
            int mid = (l + r) >> 1;
            if(check(mid, a, b, mp[d])) l = ans = mid;
            else r = mid;
        }
        printf("%d\n", ans);
    }
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}
View Code

 

posted @ 2017-11-19 19:18  19992147  阅读(169)  评论(0编辑  收藏  举报