先把所有串按顺序放到一起,两个串间加非法字符隔开,求一个后缀数组。
然后对于询问,满足条件的子串在后缀数组上一定是连续一段区间。
这个区间的左右端点可以在读入的过程中二分求。
然后这个问题变成了多组询问求一段区间内不同的数的个数。
莫队裸题。
慢着,每个元素的出现次数怎么求呀?
莫队时维护,设共有cnt个询问,那么第i个询问时一个元素的出现状态的改变会影响到cnt-i+1个询问中该元素的出现状态。因此答案累加相应次数就行了。
#include <bits/stdc++.h>
using namespace std;
#define N 210000
#define A 11000
int n,m,len,cnt,sz;
int a[N],bel[N],sa[N],has[N],tr[N],rank[N],ans[N];
int num[N],sum,a1[N];
int cmp(int x,int y,int k)
{
if(x+k>len||y+k>len)return 0;
return rank[x]==rank[y]&&rank[x+k]==rank[y+k];
}
void getsa()
{
int i,cnt;
for(i=1;i<=len;i++)has[a[i]]++;
for(i=0,cnt=0;i<=A;i++)if(has[i])tr[i]=++cnt;
for(i=1;i<=A;i++)has[i]+=has[i-1];
for(i=1;i<=len;i++)rank[i]=tr[a[i]],sa[has[a[i]]--]=i;
for(int k=1;cnt!=len;k<<=1)
{
for(i=1;i<=len;i++)has[i]=0;
for(i=1;i<=len;i++)has[rank[i]]++;
for(i=1;i<=len;i++)has[i]+=has[i-1];
for(i=len;i>=1;i--)if(sa[i]>k)tr[sa[i]-k]=has[rank[sa[i]-k]]--;
for(i=1;i<=k;i++)tr[len-i+1]=has[rank[len-i+1]]--;
for(i=1;i<=len;i++)sa[tr[i]]=i;
for(i=1,cnt=0;i<=len;i++)tr[sa[i]]=cmp(sa[i],sa[i-1],k) ? cnt:++cnt;
for(i=1;i<=len;i++)rank[i]=tr[i];
}
}
struct node
{
int l,r,pos;
node(){}
node(int l,int r,int pos):l(l),r(r),pos(pos){}
friend bool operator < (const node &r1,const node &r2)
{
if(r1.l/sz==r2.l/sz)return r1.r<r2.r;
return r1.l/sz<r2.l/sz;
};
}p[N];
void insert(int x,int tp,int now)
{
int t=bel[sa[x]];
num[t]+=tp;
if(tp==1&&num[t]==1)sum++,a1[t]+=(cnt-now+1);
if(tp==-1&&num[t]==0)sum--,a1[t]-=(cnt-now+1);
}
int main()
{
//freopen("tt.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1,l;i<=n;i++)
for(int j=0;j<=1;j++)
{
scanf("%d",&l);
for(int k=1;k<=l;k++)
scanf("%d",&a[++len]),bel[len]=i;
a[++len]=A;
}
sz=sqrt(len)+1;
getsa();
for(int n1,now=1;now<=m;now++)
{
scanf("%d",&n1);
int ln=1,rn=len;
for(int i=1,x;i<=n1;i++)
{
scanf("%d",&x);
int l=ln,r=rn,lt;
while(l<=r)
{
int mid=(l+r)>>1;
if(a[sa[mid]+i-1]<x)l=mid+1;
else r=mid-1;
}
lt=l;l=ln,r=rn;
while(l<=r)
{
int mid=(l+r)>>1;
if(a[sa[mid]+i-1]<=x)l=mid+1;
else r=mid-1;
}
rn=r;ln=lt;
}
if(ln<=rn)p[++cnt]=node(ln,rn,now);
}
sort(p+1,p+1+cnt);
for(int i=1,l=1,r=0;i<=cnt;i++)
{
while(l<p[i].l)insert(l,-1,i),l++;
while(l>p[i].l)l--,insert(l,1,i);
while(r<p[i].r)r++,insert(r,1,i);
while(r>p[i].r)insert(r,-1,i),r--;
ans[p[i].pos]=sum;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
for(int i=1;i<=n;i++)
printf("%d%c",a1[i],i==n ? '\n':' ');
return 0;
}

本文介绍了一种使用后缀数组和莫队算法解决字符串查询问题的方法。通过构建后缀数组并应用莫队算法来高效处理多个查询,求解不同数目的子串区间问题。
579

被折叠的 条评论
为什么被折叠?



