子串

题目大意

这里写图片描述

SA+莫队

我们把所有字符串连起来,中间有分割符分开,然后求SA。
那么,一个子串所在第几个单词可以看以其为前缀的后缀包含了几个分隔符。
对于每个询问,我们二分找出在SA上的对应区间。于是,询问可以改成这样:求[l,r]权值在[x,y]的权值种类数。
可以用莫队做,用分块维护就可以消掉log,这是经典套路。

#include<cstdio> 
#include<algorithm>
#include<ctime> 
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=500000+10,B=550,BB=250;
char h[maxn];
int s[maxn*2],num[maxn*2],sa[maxn*2],rank[maxn*4];
int b[maxn*2],c[maxn*2],d[maxn*2];
struct dong{
    int l,r,x,y,id;
    bool p;
} ask[maxn];
int belong[maxn*2],bel[maxn],cnt[maxn/BB+10],a[maxn],ans[maxn],sta[100];
int i,j,k,l,r,ll,rr,mid,t,n,m,mx,len,top;
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void getsa(){
    fo(i,1,n) rank[i]=s[i];
    j=1;
    while (j<=n){
        fo(i,0,n) b[i]=0;
        fo(i,1,n) b[rank[i+j]]++;
        fo(i,1,n) b[i]+=b[i-1];
        fd(i,n,1) c[b[rank[i+j]]--]=i;
        fo(i,0,n) b[i]=0;
        fo(i,1,n) b[rank[i]]++;
        fo(i,1,n) b[i]+=b[i-1];
        fd(i,n,1) d[b[rank[c[i]]]--]=c[i];
        t=0;
        fo(i,1,n){
            if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]+j]!=rank[d[i-1]+j]) t++;
            c[d[i]]=t;
        }
        fo(i,1,n) rank[i]=c[i];
        if (t==n) break;
        j*=2;
    }
    fo(i,1,n) sa[rank[i]]=i;
}
bool cmp(dong a,dong b){
    return belong[a.l]<belong[b.l]||belong[a.l]==belong[b.l]&&a.r<b.r;
}
void in(int x){
    a[x]++;
    if (a[x]==1) cnt[bel[x]]++;
}
void out(int x){
    a[x]--;
    if (!a[x]) cnt[bel[x]]--;
}
int query(int j,int k){
    int i,l=bel[j],r=bel[k],t=0;
    if (r-l<=1){
        fo(i,j,k)
            if (a[i]>0) t++;
        return t;
    }
    fo(i,l+1,r-1) t+=cnt[i];
    fo(i,j,l*BB) 
        if (a[i]>0) t++;
    fo(i,(r-1)*BB+1,k)
        if (a[i]>0) t++;
    return t;
}
void write(int x){
    if (!x){
        putchar('0');
        putchar('\n');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top) putchar(sta[top--]+'0');
    putchar('\n');
}
int main(){
    n=read();mx=m=read();
    fo(i,1,n){
        scanf("%s",h+1);
        fo(j,len+1,len+strlen(h+1)) s[j]=h[j-len]-'a'+1;
        len+=strlen(h+1);
        s[++len]=3;
    }
    n=len;
    fd(i,n,1) num[i]=num[i+1]+(s[i]==3);
    getsa();
    fo(i,1,m){
        ask[i].x=num[1]-read()+1;ask[i].y=num[1]-read()+1;
        swap(ask[i].x,ask[i].y);
        ask[i].id=i;
        scanf("%s",h+1);
        l=1;r=n;
        fo(j,1,strlen(h+1)){
            ll=l;rr=r;
            while (ll<rr){
                mid=(ll+rr)/2;
                if (s[sa[mid]+j-1]<h[j]-'a'+1) ll=mid+1;else rr=mid;
            }
            k=ll;
            if (s[sa[k]+j-1]!=h[j]-'a'+1){
                ask[i].p=1;
                break;
            }
            ll=l;rr=r;
            while (ll<rr){
                mid=(ll+rr+1)/2;
                if (s[sa[mid]+j-1]>h[j]-'a'+1) rr=mid-1;else ll=mid;
            }
            t=ll;
            if (s[sa[t]+j-1]!=h[j]-'a'+1){
                ask[i].p=1;
                break;
            }
            l=k;r=t;
        }
        ask[i].l=l;ask[i].r=r;
    }
    t=0;
    fo(i,1,m)
        if (!ask[i].p) ask[++t]=ask[i];
    m=t;
    fo(i,1,n) belong[i]=(i-1)/B+1;
    fo(i,1,num[1]) bel[i]=(i-1)/BB+1;
    sort(ask+1,ask+m+1,cmp);
    l=1;r=0;
    fo(i,1,m){
        while (l<ask[i].l) out(num[sa[l++]]);
        while (l>ask[i].l) in(num[sa[--l]]);
        while (r<ask[i].r) in(num[sa[++r]]);
        while (r>ask[i].r) out(num[sa[r--]]);
        ans[ask[i].id]=query(ask[i].x,ask[i].y);
    }
    fo(i,1,mx) write(ans[i]);
    //fo(i,1,mx) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值