[bzoj2534]Uva10829L-gap字符串

来源:互联网 发布:php音频上传代码 编辑:程序博客网 时间:2024/06/07 00:56

题目描述

有一种形如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;}
1 0
原创粉丝点击