前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >YbtOJ 535「后缀数组」相似子串

YbtOJ 535「后缀数组」相似子串

作者头像
yzxoi
发布2022-09-19 13:43:33
4360
发布2022-09-19 13:43:33
举报
文章被收录于专栏:OI

YbtOJ 535「后缀数组」相似子串

题目链接:YbtOJ #535

小 A 有一个长度为 n 的数字串 s,下标为 1\sim n

对于两个 长度相等 的字符串 A,B,设它们的长度为 len,则定义 A,B 相似 当且仅当对 \forall 1\le i,j\le len[A_i=A_j]=[B_i=B_j](即 A_iA_j 相等充要于 B_iB_j 相等)。

接下来小 A 会进行 q 次询问,每次询问 s 中有多少个子串与 s[l_i : r_i] 相似(包括 s[l_i : r_i] 自身)。这里的 s[l_i : r_i] 意为由 s 中第 l_i\sim r_i 个字符依次拼接得到的字符串。

强制在线

1\le n\le 10^51\le q\le 5\times10^5

Solution

对于字符串 S,可以将每种字符的上一次出现位置与此位置作差,作为本位置的值。

这样转化后如果两个字符串相似则转化后的数组完全相同。

那么也就可以转化为 [l1:][l2:]\text{LCP} 长度超过区间长度 L

注意到后缀转化后的数组同原串转化后的数组最多只会相差 10 位(每种字符第一次出现的位置有差异)。

于是我们以这 10 个位置为关键点,分段进行比较。

所以我们先对原串转化成的数组建立后缀数组,这样一来若要求解两个后缀转化成的数组的 \text{LCP},就可以直接 按关键点划分成若干段,将每一部分依次比较。

既然能够求出 \text{LCP},那么只要比较 \text{LCP} 的后一位就能比较两个不同后缀转化成的数组的字典序,也就可以将所有后缀转化成的数组做一遍排序。

然后,预先求出排名相邻的后缀的 \text{LCP}(类似于一般后缀数组中的 Height 数组)。

询问时在这个 “Height 数组” 上分别向左向右二分一下即可。

发现其实也并不需要使用后缀数组,求 \text{LCP} 直接 Hash+二分 也行,就是复杂度上多了只 \log

Code

代码语言:javascript
复制
/*
超!还卡哈希!
*/
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,fma")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define W while
#define I inline
#define RI register int
#define LL long long
#define Cn const
#define CI Cn int&
#define gc getchar
#define D isdigit(c=gc())
#define pc(c) putchar((c))
using namespace std;
namespace Debug{
    Tp I void _debug(Cn char* f,Ty t){cerr<<f<<'='<<t<<endl;}
    Ts I void _debug(Cn char* f,Ty x,Ar... y){W(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);}
    Tp ostream& operator<<(ostream& os,Cn vector<Ty>& V){os<<"[";for(Cn auto& vv:V) os<<vv<<",";os<<"]";return os;}
    #define gdb(...) _debug(#__VA_ARGS__,__VA_ARGS__)
}using namespace Debug;
namespace FastIO{
    Tp I void read(Ty& x){char c;int f=1;x=0;W(!D) f=c^'-'?1:-1;W(x=(x<<3)+(x<<1)+(c&15),D);x*=f;}
    Ts I void read(Ty& x,Ar&... y){read(x),read(y...);}
    Tp I void write(Ty x){x<0&&(pc('-'),x=-x,0),x<10?(pc(x+'0'),0):(write(x/10),pc(x%10+'0'),0);}
    Tp I void writeln(Cn Ty& x){write(x),pc('\n');}
}using namespace FastIO;
Cn int N=1e5+10,base=13,base2=17,P=998244353;
int n,q,a[N],rk[N],p[N],F[N][25],H[N][11],lg[N],v[N],pw[N],hsh[N],hsh2[N],pw2[N];
char s[N];vector<int> G[N];
#define pb push_back

struct ST{
    int dp[600010][51];
    void get(int *s,int n){
        for(int i=1;i<=n;i++){
            dp[i][0]=s[i];
        }
        for(int j=1;j<=(log(n)/log(2))+1;j++){
            for(int i=1;i<=n-(1<<j)+1;i++){
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int ask(int x,int y){
        int p=(log(y-x+1)/log(2));
        int Min=min(dp[x][p],dp[y-(1<<p)+1][p]);
        return Min;
    }   
}st;
struct SA {
    int rnk[N],sa[N],tme[N],sec[N],height[N],n,sigma;
    void Sort() {
        for(int i=0; i<=sigma; i++)tme[i]=0;
        for(int i=1; i<=n; i++)tme[rnk[i]]++;
        for(int i=1; i<=sigma; i++)tme[i]+=tme[i-1];
        for(int i=n; i>=1; i--)sa[tme[rnk[sec[i]]]--]=sec[i];
        memset(sec,0,sizeof(sec));
    }
    void GetSa(int *s) {
        for(int i=1; i<=n; i++)rnk[i]=s[i],sec[i]=i;
        Sort();
        for(int w=1,p=0; w<=n&&p<n; w<<=1,sigma=p) {
            p=0;
            for(int i=n-w+1; i<=n; i++)sec[++p]=i;
            for(int i=1; i<=n; i++)if(sa[i]>w)sec[++p]=sa[i]-w;
            Sort();
            swap(sec,rnk);
            rnk[sa[1]]=p=1;
            for(int i=2; i<=n; i++) {
                rnk[sa[i]]=(sec[sa[i-1]]==sec[sa[i]]&&sec[sa[i-1]+w]==sec[sa[i]+w])?  p:++p;
            }
        }
    }
    void GetHeight(int *s) {
        int k=0;
        for(int i=1; i<=n; i++) {
            if(k)k--;
            int j=sa[rnk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rnk[i]]=k;
        }
    }
    int getLcp(int l,int r){
        if(l>r){
            int t=r;
            r=l;
            l=t;
        }
        int x=min(rnk[l],rnk[r]);
        int y=max(rnk[l],rnk[r]);
        //cout<<x+1<<" "<<y<<endl;
        return st.ask(x+1,y);
    }
    int init(int *s,int len) {
        sigma=N-5;
        n=len;
        GetSa(s);
        GetHeight(s);
        st.get(height,n);
        return n;
    }
}sa;
/*————————————————
版权声明:本文为CSDN博主「YuckXi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tomyking2005/article/details/90105343
————————————————*/

I int Q(CI l,CI r){if(l>r) return 2e9;RI k=lg[r-l+1];return min(F[l][k],F[r-(1<<k)+1][k]);}
I int GetL(CI x,CI L){RI l=1,r=x,mid,X=x;W(l<=r) Q((mid=l+r>>1)+1,x)>=L?X=mid,r=mid-1:l=mid+1;return X;}
I int GetR(CI x,CI L){RI l=x,r=n,mid,X=x;W(l<=r) Q(x+1,mid=l+r>>1)>=L?X=mid,l=mid+1:r=mid-1;return X;}
I bool chk(RI x,RI y,CI mid){return (hsh[x]-1LL*hsh[x+mid]*pw[mid]%P+P)%P==(hsh[y]-1LL*hsh[y+mid]*pw[mid]%P+P)%P&&(hsh2[x]-1LL*hsh2[x+mid]*pw2[mid]%P+P)%P==(hsh2[y]-1LL*hsh2[y+mid]*pw2[mid]%P+P)%P;}
I int LCP(CI x,CI y){if(!x!ya[x]^a[y]) return 0;RI l=0,r=n-max(x,y)+1,mid,X=1;W(l<=r) chk(x,y,mid=l+r>>1)?X=mid,l=mid+1:r=mid-1;return X;}
I int Get(CI x,CI y){RI i=0,xi,yi,tx,ty,t,A=0;for(xi=x,yi=y;xi<=n&&yi<=n;++A,i++,xi=tx+1,yi=ty+1) if(tx=i<G[x].size()?G[x][i]:n+1,ty=i<G[y].size()?G[y][i]:n+1,t=sa.getLcp(xi,yi),t<min(tx-xi,ty-yi)) return A+t;else if(A+=min(tx-xi,ty-yi),(ty-yi)^(tx-xi)tx>nty>n) return A;return A;}
I bool cmp(CI x,CI y){RI i,t=Get(x,y),tx=x+t<=n?a[x+t]:-1,ty=y+t<=n?a[y+t]:-1;for(auto i:G[x]) i==x+t&&(tx=0);for(auto i:G[y]) i==y+t&&(ty=0);return tx<ty;}
I int Qry(RI x,CI L){return GetR(p[x],L)-GetL(p[x],L)+1;}
int main(){
    freopen("similar.in","r",stdin),freopen("similar.out","w",stdout);
    RI i,j,x,y,lst=0;for(read(n,q),scanf("%s",s+1),pw[0]=pw2[0]=i=1;i<=n;i++) a[i]=v[s[i]-'0']?i-v[s[i]-'0']:0,v[s[i]-'0']=i,pw[i]=1LL*pw[i-1]*base%P,pw2[i]=1LL*pw2[i-1]*base2%P;
    for(i=n;i;i--) hsh[i]=(1LL*hsh[i+1]*base%P+a[i])%P,hsh2[i]=(1LL*hsh2[i+1]*base2%P+a[i])%P;for(i=n;i;i--){for(j=0;j<=9;j++) H[i][j]=H[i+1][j];H[i][s[i]-'0']=i;for(j=0;j<=9;j++) H[i][j]&&(G[i].pb(H[i][j]),0);sort(G[i].begin(),G[i].end());}

    sa.init(a,n);

    for(i=1;i<=n;i++) rk[i]=i;for(stable_sort(rk+1,rk+n+1,cmp),i=1;i<=n;i++) p[rk[i]]=i;
    for(lg[0]=-1,i=1;i<=n;i++) lg[i]=lg[i>>1]+1,F[i][0]=Get(rk[i-1],rk[i]);for(j=1;(1<<j)<=n;j++) for(i=2;i+(1<<j)-1<=n;i++) F[i][j]=min(F[i][j-1],F[i+(1<<j-1)][j-1]);
    W(q--) read(x,y),x^=lst,y^=lst,writeln(lst=Qry(x,y-x+1));return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • YbtOJ 535「后缀数组」相似子串
    • Solution
      • Code
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档