BZOJ3230 相似子串 字符串 SA ST表
原文鏈接http://www.cnblogs.com/zhouzhendong/p/9033092.html
題目傳送門 - BZOJ3230
題意
給定字符串$s$。長度為$n$。
現在有$Q$組詢問,每組詢問內容如下:
兩個正整數$i,j$。
設$s_i,s_j$分別表示$s$的所有本質不同的子串中字典序第$i$小和第$j$小的子串。
請你輸出$|lcp(s_i,s_j)|^2+|lcs(s_i,s_j)|^2$。
如果不存在$s_i$或者$s_j$,則輸出$-1$。
$n,Q\leq 10^5$
題解
這題大概是我做的前幾題$SA$的大整合+升級版吧。
我們考慮如何找到本質不同子串中第$k$大的子串。
我們考慮按照串$s$的后綴大小從小到大處理。
對于排名為$i$的后綴$SA[i]$,考慮它 除了與排名更靠前的其他后綴 的相同前綴以外的前綴所代表的子串。
很顯然,這些子串都是本質不同的。根據后綴數組的性質,也很顯然,這些串都不同于之前已經計算過的串。不然的話,$height[i]$會更大。
而且,這些子串是按照字典序排的。
現在,我們發現這些后,不需要處理出所有子串。
統計原串后綴排名前$i$的后綴所包含的子串的個數,記為$presum[i]$。
顯然,$presum[i]=presum[i-1]+len(SA[i])-height[i]$。
于是我們在查找第$k$大子串的時候就可以通過二分或者倍增來快速地找到第$k$大的子串所在的后綴,然后確定第$k$大的子串就容易了。
至于求兩個子串的$LCP$和$LCS$長度,是后綴數組的經典操作,這里就不加贅述了。
代碼
#include <bits/stdc++.h> #define rank r_a_n_k using namespace std; typedef long long LL; const int N=200005; int n,Q; int SA[N],rank[N],height[N],tax[N],tmp[N]; int sSA[N],srank[N],sheight[N]; int ST[N][19]; LL presum[N]; char s[N]; void Sort(int n,int m,int SA[],int rank[]){for (int i=0;i<=m;i++)tax[i]=0;for (int i=1;i<=n;i++)tax[rank[i]]++;for (int i=1;i<=m;i++)tax[i]+=tax[i-1];for (int i=n;i>=1;i--)SA[tax[rank[tmp[i]]]--]=tmp[i]; } bool cmp(int rk[],int x,int y,int w){return rk[x]==rk[y]&&rk[x+w]==rk[y+w]; } void Suffix_Array(char s[],int n,int SA[],int rank[],int height[]){memset(SA,0,sizeof SA);memset(tmp,0,sizeof tmp);memset(rank,0,sizeof rank);memset(height,0,sizeof height);for (int i=1;i<=n;i++)rank[i]=s[i],tmp[i]=i;int m=127;Sort(n,m,SA,rank);for (int w=1,p=0;p<n;w<<=1,m=p){p=0;for (int i=n-w+1;i<=n;i++)tmp[++p]=i;for (int i=1;i<=n;i++)if (SA[i]>w)tmp[++p]=SA[i]-w;Sort(n,m,SA,rank);for (int i=1;i<=n;i++)swap(rank[i],tmp[i]);rank[SA[1]]=p=1;for (int i=2;i<=n;i++)rank[SA[i]]=cmp(tmp,SA[i],SA[i-1],w)?p:++p;}for (int i=1,j,k=0;i<=n;height[rank[i++]]=k)for (k=max(k-1,0),j=SA[rank[i]-1];s[i+k]==s[j+k];k++);height[1]=0; } void Get_ST(int n){memset(ST,0,sizeof ST);for (int i=1;i<=n;i++){ST[i][0]=height[i];for (int j=1;j<19;j++){ST[i][j]=ST[i][j-1];if (i-(1<<(j-1))>0)ST[i][j]=min(ST[i][j],ST[i-(1<<(j-1))][j-1]);}} } int Query(int L,int R){int val=floor(log(R-L+1)/log(2));return min(ST[L+(1<<val)-1][val],ST[R][val]); } int LCP(int x,int y){if (x==y)return n;x=rank[x],y=rank[y];return Query(min(x,y)+1,max(x,y)); } int LCS(int x,int y){return LCP(n*2+2-x,n*2+2-y); } void GetSubstr(LL k,int &L,int &R){int pos,i;for (pos=0,i=19;i>=0;i--)if (pos+(1<<i)<n&&presum[pos+(1<<i)]<k)pos+=(1<<i);L=sSA[pos+1];R=1LL*L+k-presum[pos]+sheight[pos+1]-1; } int main(){scanf("%d%d",&n,&Q);scanf("%s",s+1);s[n+1]='#';for (int i=n*2+1;i>n+1;i--)s[i]=s[n*2+2-i];Suffix_Array(s,n*2+1,SA,rank,height);Get_ST(n*2+1);for (int i=n+1;i<=n*2+1;i++)s[i]=0;Suffix_Array(s,n,sSA,srank,sheight);presum[0]=0;for (int i=1;i<=n;i++)presum[i]=presum[i-1]+(n-sSA[i]+1)-sheight[i];while (Q--){LL k1,k2;int L1,R1,L2,R2;scanf("%lld%lld",&k1,&k2);if (k1>presum[n]||k2>presum[n]){puts("-1");continue;}GetSubstr(k1,L1,R1);GetSubstr(k2,L2,R2);LL len=min(R1-L1+1,R2-L2+1);LL lcp=min(len,(LL)LCP(L1,L2)),lcs=min(len,(LL)LCS(R1,R2));printf("%lld\n",lcp*lcp+lcs*lcs);}return 0; }
轉載于:https://www.cnblogs.com/zhouzhendong/p/BZOJ3230.html
總結
以上是生活随笔為你收集整理的BZOJ3230 相似子串 字符串 SA ST表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu安装qtcreator
- 下一篇: SNF软件开发机器人-子系统-功能-【列