zoukankan      html  css  js  c++  java
  • 洛谷 P4770 [NOI2018]你的名字

    简明题意:你有一个字符串(S),每次询问一个字符串(T)和区间([l,r]),求(T)中不在(S[l,r])中出现的本质不同的子串个数

    不在(S[l,r])中出现可以转化成(T)的所有子串减去在(S[l,r])中出现

    首先考虑(l=1,r=|S|)时的做法

    我们对于(T)的每一个前缀([1,i])计算出最长的是(S)的子串的后缀长度

    这个很好求,先对(S)做后缀自动机,假设我们已经匹配到了(T)串的第i个字符,当前最长匹配长度为l,如果(S)的parent树上有出边为(T_i),那么就走到这个点,并且l++;否则就往上跳直到有(T_i)这条出边为止,然后l变为这个点的len然后再继续匹配就可以;跳到根还没有出边的话当前l就为0

    然后要求(T)的在(S[l,r])出现的本质不同的子串个数

    既然我们已经得到了上面的那个东西,那么只要计算出(T)的每个节点出现的本质不同的子串个数就可以了,这个直接对(T)做后缀自动机,然后([max(len_{fa_x},l),len_x])都可以算作答案

    那么推广到这个题上来

    思考上面在(S)的parent树上跳的那个过程,现在的是区间([l,r]),那么可能(fa_x)不在([l,r])里,也就是比l小,这样我们是不能转移的,所以我们考虑这个点的endpos的集合,我们应该找到一个位置p使得(p-len_x+1geq l),那么为了便于维护,我们要找到一个最大的(p-len_x+1),那也就是p最大

    于是这就是一个二维偏序,第一维是(pleq l),第二维是p要在x的子树中

    第一维直接对询问离线,然后对右端点排序,往右加点就可以

    第二维考虑对parent树dfs,然后这就是一个区间问题了

    有一点要注意的是,当(p-len_x+1le l)时,x的父亲可能会有左端点比l大,但是长度越过了l,跳点的时候还要加上这种情况

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define zrt k << 1
    #define yrt k << 1 | 1
    const int N = 5e5;
    using namespace std;
    int n,m,T,nxt[N * 2 + 5],head[N * 2 + 5],edge[N * 2 + 5],edge_cnt;
    int size[N * 2 + 5],dfn_cnt,dfn[N * 2 + 5],f[N * 2 + 5];
    long long ans[N + 5];
    char s[N + 5],t[N * 2 + 5];
    struct Q
    {
        int l,r,st,ed,id;
    }q[N + 5];
    struct sam
    {
        int fa[N * 2 + 5],len[N * 2 + 5],cnt,las,ch[N * 2 + 5][26],ep[N * 2 + 5],mp[N * 2 + 5];
        sam()
        {
            cnt = las = 1;
        }
        void clear()
        {
            for (int i = 1;i <= cnt;i++)
            {
                ep[i] = mp[i] = len[i] = fa[i] = 0;
                for (int j = 0;j < 26;j++)
                    ch[i][j] = 0;
            }
            cnt = las = 1;
        }
        void insert(int c,int id)
        {
            int q = ++cnt,p = las;
            las = q;
            len[q] = len[p] + 1;
            ep[q] = id;
            mp[id] = q;
            while (p && ! ch[p][c])
            {
                ch[p][c] = q;
                p = fa[p];
            }
            if (!p)
                fa[q] = 1;
            else
            {
                int x = ch[p][c];
                if (len[x] == len[p] + 1)
                    fa[q] = x;
                else
                {
                    int y = ++cnt;
                    ep[y] = id;
                    len[y] = len[p] + 1;
                    fa[y] = fa[x];
                    fa[x] = fa[q] = y;
                    for (int i = 0;i < 26;i++)
                        ch[y][i] = ch[x][i];
                    while (p && ch[p][c] == x)
                    {
                        ch[p][c] = y;
                        p = fa[p];
                    }
                }
            }
        }
    }a,b;
    int cmp(Q x,Q y)
    {
        return x.r < y.r;
    }
    void add_edge(int u,int v)
    {
        edge[++edge_cnt] = v;
        nxt[edge_cnt] = head[u];
        head[u] = edge_cnt;
    }
    void dfs(int u)
    {
        dfn[u] = ++dfn_cnt;
        size[u] = 1;
        for (int i = head[u];i;i = nxt[i])
        {
            int v = edge[i];
            dfs(v);
            size[u] += size[v];
        }
    }
    struct Seg
    {
        int mx[N * 8 + 5];
        void upd(int k)
        {
            mx[k] = max(mx[zrt],mx[yrt]);
        }
        void modify(int k,int l,int r,int x,int z)
        {
            if (l == r)
            {
                mx[k] = z;
                return;
            }
            int mid = l + r >> 1;
            if (x <= mid)
                modify(zrt,l,mid,x,z);
            else
                modify(yrt,mid + 1,r,x,z);
            upd(k);
        }
        int query(int k,int l,int r,int x,int y)
        {
            if (l >= x && r <= y)
                return mx[k];
            int mid = l + r >> 1;
            if (x > mid)
                return query(yrt,mid + 1,r,x,y);
            else
                if (y <= mid)
                    return query(zrt,l,mid,x,y);
                else
                    return max(query(zrt,l,mid,x,y),query(yrt,mid + 1,r,x,y));
        }
    }tree;
    int main()
    {
        scanf("%s",s + 1);
        n = strlen(s + 1);
        for (int i = 1;i <= n;i++)
            a.insert(s[i] - 'a',i);
        for (int i = 2;i <= a.cnt;i++)
            add_edge(a.fa[i],i);
        dfs(1);
        scanf("%d",&T);
        int l,r;
        m = 1;
        for (int i = 1;i <= T;i++)
        {
            scanf("%s",t + m);
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id = i;
            q[i].st = m;
            m += strlen(t + m);
            q[i].ed = m - 1;
        }
        sort(q + 1,q + T + 1,cmp);
        for (int i = 1,j = 1;i <= T;i++)
        {
            while (j <= q[i].r)
            {
                tree.modify(1,1,dfn_cnt,dfn[a.mp[j]],j);
                j++;
            }
            for (int k = q[i].st;k <= q[i].ed;k++)
                b.insert(t[k] - 'a',k - q[i].st + 1);
            int u = 1,ll = 0;
            for (int k = q[i].st;k <= q[i].ed;k++)
            {
                int v = a.ch[u][t[k] - 'a'];
                if (v)
                    u = v,ll++;
                else
                {
                    u = a.fa[u];
                    while (u)
                    {
                        if (a.ch[u][t[k] - 'a'])
                        {
                            ll = a.len[u] + 1;
                            u = a.ch[u][t[k] - 'a'];
                            break;
                        }
                        u = a.fa[u];
                    }
                    if (!u)
                        u = 1,ll = 0;
                }
                while (u)
                {
                    int mx = tree.query(1,1,dfn_cnt,dfn[u],dfn[u] + size[u] - 1);
                    if (mx && mx - ll + 1 >= q[i].l)
                        break;
                    if (mx && mx - a.len[a.fa[u]] + 1 > q[i].l)
                    {
                        ll = mx - q[i].l + 1;
                        break;
                    }
                    u = a.fa[u];
                    ll = a.len[u];
                }
                f[k - q[i].st + 1] = ll;
            }
            for (int k = 2;k <= b.cnt;k++)
                ans[q[i].id] += max(b.len[k] - max(f[b.ep[k]],b.len[b.fa[k]]),0);
            b.clear();
        }
        for (int i = 1;i <= T;i++)
            printf("%lld
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    42. Trapping Rain Water
    223. Rectangle Area
    645. Set Mismatch
    541. Reverse String II
    675. Cut Off Trees for Golf Event
    安装 VsCode 插件安装以及配置
    向上取整 向下取整 四舍五入 产生100以内随机数
    JS 判断是否为数字 数字型特殊值
    移动端初始配置,兼容不同浏览器的渲染内核
    Flex移动布局中单行和双行布局的区别以及使用
  • 原文地址:https://www.cnblogs.com/sdlang/p/13073720.html
Copyright © 2011-2022 走看看