题目描述
有一种形如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;
}