[bzoj2534]Uva10829L-gap字符串

题目描述

有一种形如uvu形式的字符串,其中u是非空字符串,且V的长度正好为L,那么称这个字符串为L-Gap字符串
给出一个字符串S,以及一个正整数L,问S中有多少个L-Gap子串.

L≤10 字符串长≤50000

分析

这道题难度挺大的。。。
考虑一个合法的UVU串。假设两个U结束位置分别是i,j(i < j)。
设两个前缀i,j的最长公共后缀长度为len。那么i,j合法时满足以下两个条件:
1. i+L < j
2. i+L+len≥j

现在考虑给字符串建后缀自动机,然后对于两个前缀,分别是i和j,在sam上跑到两个不同节点,这两个节点在fail树上lca所表示最长后缀的长度就是i,j的最长公共后缀了。
那么一个解法就出来了:用平衡树维护一棵子树所有的前缀,递归完所有儿子后,每个平衡树两两合并,并且合并前其中一棵子树的所有前缀拿出来到另一个平衡树上查询。用启发式合并即可做到 O(nlog2n)
我用的是treap

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=50005,M=N*2;

typedef long long LL;

int n,m,L,tot,last,e[M][256],step[M],fail[M],h[M],E[M],nxt[M],root[M],son[N][2],fix[N],key[N],size[N],fa[N],D[N];

LL ans;

char s[N];

void Extend(int c)
{
    int np=++m,p=last,q,nq;
    last=np;
    step[np]=step[p]+1;
    for (;p>=0 && !e[p][c];p=fail[p]) e[p][c]=np;
    if (p<0) fail[np]=0;else
    {
        q=e[p][c];
        if (step[p]==step[q]-1) fail[np]=q;else
        {
            nq=++m;
            fail[nq]=fail[q]; memcpy(e[nq],e[q],sizeof(e[q]));
            step[nq]=step[p]+1;
            fail[np]=fail[q]=nq;
            for (;p>=0 && e[p][c]==q;p=fail[p]) e[p][c]=nq;
        }
    }
}

void add(int x,int y)
{
    E[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
}

void calc(int x,int y,int sig)
{
    if (!x) return;
    if (key[x]>y) calc(son[x][0],y,sig);
    else
    {
        ans+=sig*size[son[x][0]]+sig;
        if (key[x]<y) calc(son[x][1],y,sig);
    }
}

void Rotate(int x,int t)
{
    int y=fa[x];
    fa[x]=fa[y];
    if (fa[y]>0)
    {
        if (son[fa[y]][0]==y) son[fa[y]][0]=x;else son[fa[y]][1]=x;
    }
    son[y][t]=son[x][t^1];
    if (son[y][t]>=0) fa[son[y][t]]=y;
    son[x][t^1]=y;
    fa[y]=x;
    size[y]=size[son[y][0]]+size[son[y][1]]+1;
    size[x]=size[son[x][0]]+size[son[x][1]]+1;
}

void insert(int x,int y)
{
    size[x]++;
    if (key[y]<key[x])
    {
        if (son[x][0])
        {
            insert(son[x][0],y);
            if (fix[x]>fix[son[x][0]]) Rotate(son[x][0],0);
        }else
        {
            son[x][0]=y; fa[y]=x;
            son[y][0]=son[y][1]=0;
            size[y]=1;
            if (fix[x]>fix[y]) Rotate(y,0);
        }
    }else
    {
        if (son[x][1])
        {
            insert(son[x][1],y);
            if (fix[x]>fix[son[x][1]]) Rotate(son[x][1],1);
        }else
        {
            son[x][1]=y; fa[y]=x;
            son[y][0]=son[y][1]=0;
            size[y]=1;
            if (fix[x]>fix[y]) Rotate(y,1);
        }
    }
}

void dfs(int x)
{
    for (int i=h[x];i;i=nxt[i])
    {
        dfs(E[i]);
        if (!root[x]) root[x]=root[E[i]];
        else if (root[E[i]])
        {
            if (size[root[x]]<size[root[E[i]]]) root[x]^=root[E[i]]^=root[x]^=root[E[i]];
            D[tot=1]=root[E[i]];
            for (int j=1;j<=tot;j++)
            {
                int k=D[j];
                if (son[k][0]) D[++tot]=son[k][0];
                if (son[k][1]) D[++tot]=son[k][1];
                k=key[k];
                calc(root[x],k+L+step[x],1); calc(root[x],k+L,-1);
                calc(root[x],k-L-1,1); calc(root[x],k-L-step[x]-1,-1);
            }
            for (int j=1;j<=tot;j++)
            {
                insert(root[x],D[j]);
                if (fa[D[j]]==0) root[x]=D[j];
            }
        }
    }
}

int main()
{
    srand(65870762);
    scanf("%d%s",&L,s+1);
    fail[0]=-1;
    n=strlen(s+1);
    for (int i=1;i<=n;i++) Extend(s[i]);
    for (int i=1;i<=m;i++) add(fail[i],i);
    tot=0;
    for (int i=1,j=0;i<=n;i++)
    {
        for (;j>0 && e[j][s[i]]==0;j=fail[j]);
        if (e[j][s[i]]>0)
        {
            j=e[j][s[i]];
            root[j]=++tot;
            key[tot]=i;
            fix[tot]=rand()*109+rand();
            size[tot]=1;
        }
    }
    dfs(0);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值