Bzoj3230: 相似子串
题解:后缀数组的特性我们可以通过对于这个位置比较前一个位置的h值来解决以i位置为起点的后缀能产生多少个和前面的串不同的子串 所以问题转化为 求第k大出现的位置 二分找就ok 然后找出来的位置求一下lcp 然后反过来再求一下就ok
#include <bits/stdc++.h> #define ll long long const int MAXN=1e5+10; using namespace std; char s1[MAXN],s2[MAXN]; int txt[MAXN],sa[MAXN],td[MAXN],rank1[MAXN],rank2[MAXN],t1[MAXN],t2[MAXN]; int dp[MAXN][21];int mu[21];int ma[MAXN]; bool cmp(int f[],int t,int w,int k){return f[t]==f[w]&&f[t+k]==f[w+k];} ll ans[MAXN]; void Sa(char str[]){ // cout<<str<<endl; int len=strlen(str);int m=128; int *td=t1;int *rank1=t2; for(int i=0;i<m;i++)txt[i]=0; for(int i=0;i<len;i++)rank1[i]=str[i],txt[str[i]]++; for(int i=1;i<m;i++)txt[i]+=txt[i-1]; for(int i=len-1;i>=0;i--)sa[--txt[str[i]]]=i; for(int k=1;k<=len;k=k*2){ int p=0; for(int i=len-k;i<len;i++)td[p++]=i; for(int i=0;i<len;i++)if(sa[i]>=k)td[p++]=sa[i]-k; for(int i=0;i<m;i++)txt[i]=0; for(int i=0;i<len;i++)txt[rank1[i]]++; for(int i=1;i<m;i++)txt[i]+=txt[i-1]; for(int i=len-1;i>=0;i--)sa[--txt[rank1[td[i]]]]=td[i]; swap(rank1,td);rank1[sa[0]]=0;p=1; for(int i=1;i<len;i++)rank1[sa[i]]=cmp(td,sa[i],sa[i-1],k)?p-1:p++; if(p==len)return ; m=p; } } int h[MAXN],H[MAXN];ll sum[MAXN]; void hh(char str[]){ int len=strlen(str); for(int i=0;i<len;i++)rank2[sa[i]]=i; memset(h,0,sizeof(h)); memset(H,0,sizeof(H)); for(int i=0;i<len;i++){ if(rank2[i]==0)continue; int t=sa[rank2[i]-1];int w=i;int k; if(i==0||H[i-1]<=1)k=0; else k=H[i-1]-1,t+=k,w+=k; while(t<len&&w<len){ if(str[t]==str[w])k++; else break; t++;w++; } H[i]=k;h[rank2[i]]=k; } } void st(int len){ for(int i=1;i<len;i++)dp[i][0]=h[i]; for(int j=1;mu[j]<=len;j++){ for(int i=1;i+mu[j]<=len;i++){ dp[i][j]=min(dp[i][j-1],dp[i+mu[j-1]][j-1]); } } } int rmq(int l,int r){ if(l>r)return MAXN; int k=ma[r-l+1]; return min(dp[l][k],dp[r-mu[k]+1][k]); } typedef struct node{ ll l,r;int lx,rx,ly,ry; }node; node que[MAXN]; int q; int check(ll k,int len){ int l=1;int r=len-1; while(l<=r){ int mid=(l+r)>>1; if(sum[mid-1]<k&&sum[mid]>=k)return mid; else if(sum[mid-1]>=k)r=mid-1; else l=mid+1; } return len; } void slove(){ Sa(s1);hh(s1);int len=strlen(s1);st(len); // cout<<s1<<" "<<endl; // for(int i=1;i<len;i++)cout<<sa[i]<<" "; // cout<<endl; for(int i=1;i<len;i++){sum[i]=len-1-sa[i]-h[i];} //for(int i=1;i<len;i++)cout<<sum[i]<<" "; //cout<<endl; for(int i=1;i<len;i++)sum[i]+=sum[i-1]; for(int i=1;i<=q;i++){ // cout<<que[i].l<<" "<<que[i].r<<endl; que[i].lx=check(que[i].l,len);que[i].ly=check(que[i].r,len); // cout<<i<<" "<<que[i].lx<<" "<<que[i].ly<<endl; if(que[i].lx>=len||que[i].ly>=len){ ans[i]=-1;continue; } que[i].rx=que[i].l-sum[que[i].lx-1]+h[que[i].lx];que[i].ry=que[i].r-sum[que[i].ly-1]+h[que[i].ly]; // cout<<que[i].rx<<"----"<<que[i].ry<<endl; ll k1=min(que[i].rx,min(rmq(que[i].lx+1,que[i].ly),que[i].ry)); // cout<<k1<<" "<<endl; ans[i]+=1ll*k1*k1; que[i].rx=(que[i].rx-1)+sa[que[i].lx]; que[i].lx=que[i].rx-sa[que[i].lx]+1; que[i].rx=len-2-que[i].rx; que[i].ry=(que[i].ry-1)+sa[que[i].ly]; que[i].ly=que[i].ry-sa[que[i].ly]+1; que[i].ry=len-2-que[i].ry; // cout<<que[i].lx<<" "<<que[i].ly<<" "<<que[i].rx<<" "<<que[i].ry<<endl; } } void slove2(){ Sa(s2);hh(s2);int len=strlen(s2);st(len); for(int i=1;i<=q;i++){ if(ans[i]==-1)continue; int l=rank2[que[i].rx];int r=rank2[que[i].ry]; if(l>r)swap(l,r);l++; ll k1=1ll*min(que[i].lx,min(que[i].ly,rmq(l,r))); ans[i]+=1ll*k1*k1; } } int main(){ mu[0]=1;for(int i=1;i<=20;i++)mu[i]=mu[i-1]<<1; ma[0]=-1;for(int i=1;i<MAXN;i++)if((i&(i-1))==0)ma[i]=ma[i-1]+1;else ma[i]=ma[i-1]; int n;scanf("%d%d",&n,&q); scanf(" %s",s1);int len=strlen(s1);s1[len++]='$'; for(int i=0;i<len-1;i++)s2[i]=s1[len-i-2];s2[len-1]='$'; // cout<<s2<<endl; for(int i=1;i<=q;i++)scanf("%lld%lld",&que[i].l,&que[i].r); slove();slove2(); for(int i=1;i<=q;i++)printf("%lld\n",ans[i]); }
3230: 相似子串
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1980 Solved: 515
[Submit][Status][Discuss]
Description
Input
输入第1行,包含3个整数N,Q。Q代表询问组数。
第2行是字符串S。
接下来Q行,每行两个整数i和j。(1≤i≤j)。
Output
输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。
Sample Input
5 3
ababa
3 5
5 9
8 10
ababa
3 5
5 9
8 10
Sample Output
18
16
-1
16
-1
HINT
样例解释
第1组询问:两个子串是“aba”,“ababa”。f = 32 + 32 = 18。
第2组询问:两个子串是“ababa”,“baba”。f = 02 + 42 = 16。
第3组询问:不存在第10个子串。输出-1。
数据范围
N≤100000,Q≤100000,字符串只由小写字母'a'~'z'组成