P4770 [NOI2018]你的名字

本文深入探讨了一个字符串匹配问题,通过SAM(Suffix Array Mania)算法解决特定串匹配需求。介绍了如何构建后缀数组,并利用二分平衡树进行区间查询,以高效找出不在目标子串中的本质不同子串数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

蒟蒻表示不会sam凉凉了,所以只能提高SA技巧?

题意:有一个串\(A\),每次选择一个\(A\)的子串\(A'\),以及串\(B\),问\(B\)的所有本质不同子串中不在\(A'\)中的串的数量。

(定义\(A_i\)表示以字符\(A_i\)开头的后缀,\(B_i\)同理)

\(B\)的本质不同字串显然是\(|B|*(|B|+1)/2\)了,然后要减去本质不同的在\(A'\)中的串

首先把所有串拼一起,把SA建出来,辣么就可以在SA中找到\(A'\)所对应的所有后缀,对于\(B\)对应的每个后缀\(B_i\),计算\(\max_{j=l}^r LCP(A_j,B_i)\),就是在\(A'\)中的前缀数量,加起来就是这个东西了,由于还要判重,所以计入的其实是\(\max(0,\max_{j=l}^r LCP(A_j,B_i)-LCP(B_i,next(B_{i})))\),其中\(next(B_i)\)意思是在\(B\)串的SA上\(B_i\)的后继

\(LCP(B_i,next(B_{i}))\)随便算是吧,现在要算的是\(\max_{j=l}^rLCP(A_j,B_i)\)

要算LCP的max值,显然只要在SA上求出前驱和后继计算就行了,那么要算区间前驱后继,就是二逼平衡树了

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
#define Log(x) (31-__builtin_clz(x))
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
char S[1600037];
int t[100010],lent[100010];
int n,N;
int ql[100010],qr[100010];
namespace SA{
    int x[1600037],y[1600037],_[1600037],SA[1600037],rk[1600037],ht[1600037],t[1600037];
    int st[21][1600037];
    il int LCP(int x,int y){
        if(!x||!y)return 0;
        if(x==y)return 1e9;
        int l=Log(y-x);
        return std::min(st[l][x],st[l][y-(1<<l)]);
    }
    il int gety(int x){return x<=N?y[x]:-1;}
    il vd getSA(){
        int set=128;
        for(int i=1;i<=N;++i)++t[x[i]=S[i]];
        for(int i=1;i<=set;++i)t[i]+=t[i-1];
        for(int i=N;i;--i)SA[t[x[i]]--]=i;
        for(int k=1;k<=N;k<<=1){
            int p=0;
            for(int i=N-k+1;i<=N;++i)y[++p]=i;
            for(int i=1;i<=N;++i)if(SA[i]>k)y[++p]=SA[i]-k;
            for(int i=0;i<=set;++i)t[i]=0;
            for(int i=1;i<=N;++i)++t[x[y[i]]];
            for(int i=1;i<=set;++i)t[i]+=t[i-1];
            for(int i=N;i;--i)SA[t[x[y[i]]]--]=y[i];
            memcpy(_,x,sizeof _);
            memcpy(x,y,sizeof _);
            memcpy(y,_,sizeof _);
            x[SA[1]]=p=1;
            for(int i=2;i<=N;++i){
                if(gety(SA[i])!=gety(SA[i-1])||gety(SA[i]+k)!=gety(SA[i-1]+k))++p;
                x[SA[i]]=p;
            }
            if(p>=N)break;set=p;
        }
        for(int i=1;i<=N;++i)rk[SA[i]]=i;
        for(int i=1,j,k=0;i<=N;++i){
            if(rk[i]==N)continue;
            if(k)--k;
            j=SA[rk[i]+1];
            while(S[i+k]==S[j+k])++k;
            ht[rk[i]]=k;
        }
        for(int i=1;i<=N;++i)st[0][i]=ht[i];
        for(int i=1;i<=Log(N);++i)
            for(int j=1;j+(1<<i)-1<=N;++j)
                st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<i-1)]);
        //for(int i=1;i<=N;++i)printf("%d %s\n",ht[i],S+SA[i]);
    }
}
#define mid ((l+r)>>1)
int rt[500010],ls[20000010],rs[20000010],sum[20000010],cnt;
il vd build(int&x,int l,int r){
    x=++cnt;if(l==r)return;
    build(ls[x],l,mid),build(rs[x],mid+1,r);
}
il vd update(int&x,int l,int r,const int&p){
    ++cnt;ls[cnt]=ls[x],rs[cnt]=rs[x],sum[cnt]=sum[x];x=cnt;
    ++sum[x];if(l==r)return;
    if(p<=mid)update(ls[x],l,mid,p);
    else update(rs[x],mid+1,r,p);
}
int pp;
il int query_nxt(int x,int y,int l,int r){
    if(!(sum[y]-sum[x]))return 0;
    if(l==r)return l;
    if(mid<pp)return query_nxt(rs[x],rs[y],mid+1,r);
    if(pp<l){
        if(sum[ls[y]]-sum[ls[x]])return query_nxt(ls[x],ls[y],l,mid);
        else return query_nxt(rs[x],rs[y],mid+1,r);
    }else{
        int t=query_nxt(ls[x],ls[y],l,mid);
        return t?t:query_nxt(rs[x],rs[y],mid+1,r);
    }
}
il int query_pre(int x,int y,int l,int r){
    if(!(sum[y]-sum[x]))return 0;
    if(l==r)return l;
    if(pp<=mid)return query_pre(ls[x],ls[y],l,mid);
    if(r<pp){
        if(sum[rs[y]]-sum[rs[x]])return query_pre(rs[x],rs[y],mid+1,r);
        else return query_pre(ls[x],ls[y],l,mid);
    }else{
        int t=query_pre(rs[x],rs[y],mid+1,r);
        return t?t:query_pre(ls[x],ls[y],l,mid);
    }
}
#undef mid
il bool check(int l,int r,int p,int k){
    if(l>r)return 0;
    pp=p;
    if(SA::LCP(query_pre(rt[l-1],rt[r],1,N),p)>=k)return 1;
    if(SA::LCP(p,query_nxt(rt[l-1],rt[r],1,N))>=k)return 1;
    return 0;
}
int lcp[1600037];
int main(){
#ifdef XZZSB
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    scanf("%s",S+1);n=strlen(S+1);N=n+1;
    int Q=gi();t[1]=n+1;S[n+1]='~';
    int sumt=0;
    for(int i=1;i<=Q;++i){
        scanf("%s",S+t[i]+1);ql[i]=gi(),qr[i]=gi();
        lent[i]=strlen(S+t[i]+1);t[i+1]=t[i]+lent[i]+1;N+=lent[i]+1;
        sumt+=lent[i];
        S[t[i]+lent[i]+1]='|';
    }
    SA::getSA();
    build(rt[0],1,N);
    for(int i=1;i<=n;++i)rt[i]=rt[i-1],update(rt[i],1,N,SA::rk[i]);
    int l,r;
    for(int o=1;o<=Q;++o){
        l=ql[o],r=qr[o];
        std::vector<int>sufs;
        for(int i=t[o]+1;i<=t[o]+lent[o];++i)sufs.push_back(SA::rk[i]);
        std::sort(sufs.begin(),sufs.end());
        ll res=1ll*lent[o]*(lent[o]+1)/2;
        for(int i=1;i<sufs.size();++i)res-=(lcp[SA::SA[sufs[i-1]]]=SA::LCP(sufs[i-1],sufs[i]));
        for(int i=t[o]+1,j=0;i<=t[o]+lent[o];++i){
            if(j)--j;
            while(check(l,r-j,SA::rk[i],j+1))++j;
            res-=std::max(0,j-lcp[i]);
        }
        printf("%lld\n",res);
    }
    return 0;
}

转载于:https://www.cnblogs.com/xzz_233/p/10651945.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值