【题目】
原题地址
给定一个字符串
,多组询问给定字符串
以及两个数字
。求
中有多少个子串
满足:
的任意一个子串没有在
中出现过。
【解题思路】
加深对
的理解。
既然是字符串题,我们首先对
和
分别建
。
考虑
的情况。
设
为
能匹配
的最长后缀为
(若
则
没有在
中出现过)。那么这个我们在
上一路往下跑,匹配不了就往
跳,这样就可以简单处理出来,有点类似双指针。
设
中节点
的
集合包含的字符串最大长度为
,字符串第一次出现的位置为
(这个节点
集中的每个串
都是一样的,因为是后缀包含关系,终点等价)。我们枚举
中的每个节点,考虑每条
到
边的贡献,那么:
这个式子的意思就是对于每个节点,不属于
的子串的总个数为当前节点所代表的集合字符串个数减去与
有匹配的字符串个数。
于是现在实际上总的问题就是求这个
。
我们可以对于
的
每个节点按
排序,每个节点建线段树表示出现位置,做线段树合并。那么每次我们求
,往下扩展的时候,我么只需要判断这个节点线段树中
是否出现过即可。其中
表示当前匹配了
,出现了即表示字符串出现在
内。
复杂度 ,我们取 是 级别吧。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10,M=N*20;
int rt[N],b[N],c[N],lim[N];
char s[N];
ll ans;
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
struct Segment
{
int sz,ls[M],rs[M];
void insert(int &x,int l,int r,int p)
{
if(!x) x=++sz;
if(l==r) return;
int mid=(l+r)>>1;
if(p<=mid) insert(ls[x],l,mid,p);
else insert(rs[x],mid+1,r,p);
}
int merge(int x,int y)
{
if(!x || !y) return x+y;
int z=++sz;
ls[z]=merge(ls[x],ls[y]);rs[z]=merge(rs[x],rs[y]);
return z;
}
bool query(int x,int l,int r,int L,int R)
{
if(!x) return 0;
if(L<=l && r<=R) return 1;
int mid=(l+r)>>1;bool res=0;
if(L<=mid) res|=query(ls[x],l,mid,L,R);
if(R>mid) res|=query(rs[x],mid+1,r,L,R);
return res;
}
}tr;
struct SAM
{
int sz,tot,las,n,mx[N],fa[N],pos[N],ch[N][26];
void extend(int x,int c)
{
int p,np,q,nq;
p=las;las=np=++sz;mx[np]=mx[p]+1;pos[np]=c;
for(;p && !ch[p][x];p=fa[p]) ch[p][x]=np;
if(!p) fa[np]=1;
else
{
q=ch[p][x];
if(mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++sz;mx[nq]=mx[p]+1;pos[nq]=pos[q];
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void reset(int l)
{
memset(ch,0,(sz+2)*sizeof(ch[0]));
sz=las=1;n=l;
}
void buildS(char *s,int l)
{
reset(l);
for(int i=1;i<=n;++i) extend(s[i]-'a',i),tr.insert(rt[las],1,n,i);
for(int i=1;i<=sz;++i) ++b[mx[i]];
for(int i=1;i<=n;++i) b[i]+=b[i-1];
for(int i=1;i<=sz;++i) c[b[mx[i]]--]=i;
for(int i=sz,x=c[i];i>1;x=c[--i]) rt[fa[x]]=tr.merge(rt[x],rt[fa[x]]);
}
void buildT(char *s,int l)
{
reset(l);
for(int i=1;i<=n;++i) extend(s[i]-'a',i);
}
}S,T;
void solve()
{
scanf("%s",s+1);
int l=read(),r=read(),len=strlen(s+1);
T.buildT(s,len);memset(lim,0,(len+2)*4);
for(int i=1,now=0,p=1;i<=len;lim[i++]=now)
{
int x=s[i]-'a';
for(;;)
{
if(!tr.query(rt[S.ch[p][x]],1,S.n,l+now,r))
{
if(!now) break; --now;
if(now==S.mx[S.fa[p]]) p=S.fa[p];
}
else {++now;p=S.ch[p][x];break;}
}
}
ll ans=0;
for(int i=2;i<=T.sz;++i)
ans+=max(0,T.mx[i]-max(T.mx[T.fa[i]],lim[T.pos[i]]));
printf("%lld\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("LGP4770.in","r",stdin);
freopen("LGP4770.out","w",stdout);
#endif
scanf("%s",s+1);int len=strlen(s+1);
S.buildS(s,len);
for(int T=read();T;--T) solve();
return 0;
}
【总结】
套路啊套路,本质啊本质。