【BZOJ4566】【HAOI2016】找相同字符
后缀数组有些性质还是比较好用的
原题:
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。
1 <=n1, n2<= 200000
恩这题不少人都说是广义后缀自动姬
后缀自动姬是很强大,后缀数组也有很多很好的性质,因为我太弱只会后缀数组所以就用后缀数组写了(当然是看题解写的
首先依照后缀数组的惯例,两个串先拼到一起,中间用$连接,后面添一个#(#比$小),然后求出后缀数组和height
使用height的性质,如果两个相邻的后缀属于不同的串(当然其中一个后缀会包含另一个串),他们的height就表示这两个串的最大公共子串
容易想到height性质的扩展,容易发现任意两个后缀(当然不能是#和$开头的)之间最小的height表示这两个后缀的最大公共子串
同时这两个后缀间的所有长度为这两个后缀之间最小height的前缀都是各不相同的子串(是否在同一个串内就不一定了)
酱紫就可以很方便地调用所有公共子串,计算答案就非常容易了
一个方法是按rank递增枚举,对与每次枚举到的i,计temp为height[i],再从i往后枚举j,如果height[j]<temp,temp就更新为height[j],然后为答案加上temp(因为位置不同,所以每个长度为temp的子串对答案的贡献是temp
这个方法是O(N^2)的,4e5会T掉
然后就要使用神奇的并查集了(我感觉并查集这个方法非常不好想,但是用后缀数组拿N^2部分分的话还不如直接kmp
先按height递减拍一下序,然后每次枚举i,把i和i-1合并并计算贡献,设i这个集合中在左边的串有ll[i]个,右边的串有rr[i]个,i和i-1对答案的贡献就是(rr[i]*ll[i-1]+rr[i-1]*ll[i])*height[i]
因为按height递减排序了,就不用考虑height限制的问题了(有点cdq分治的意思
并查集真神奇
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<vector> 7 using namespace std; 8 int n,m; char s[410000]; int N; 9 int tp[410000],ll[410000],rr[410000]; 10 int gttp(int x){ while(tp[x]!=x) x=tp[x]; return x;} 11 void mg(int x,int y){ 12 x=gttp(x),y=gttp(y); 13 tp[x]=y,ll[y]+=ll[x],rr[y]+=rr[x]; 14 } 15 int rk[410000],hght[410000]; 16 int cnt[220],cntrk[410000]; 17 int rk1[410000],rk2[410000]; 18 int sa[410000],tmpsa[410000]; 19 void gtsffxrk(){ 20 memset(cnt,0,sizeof(cnt)); 21 int i,l; bool flg; 22 for( i=0;i<N;++i) ++cnt[s[i]]; 23 for( i=1;i<=210;++i) cnt[i]+=cnt[i-1]; 24 for( i=0;i<N;++i) rk[i]=cnt[s[i]]-1;//为了把$降到0,似乎如果出现#依旧可以使用 25 for( l=1;l<N;l<<=1){ 26 for( i=0;i<N;++i) rk1[i]=rk[i],rk2[i]=(i+l<N)?rk[i+l]:0; 27 fill(cntrk,cntrk+N,0); 28 for( i=0;i<N;++i) ++cntrk[rk2[i]]; 29 for( i=1;i<N;++i) cntrk[i]+=cntrk[i-1]; 30 for( i=N-1;i>=0;--i) tmpsa[--cntrk[rk2[i]]]=i; 31 fill(cntrk,cntrk+N,0); 32 for( i=0;i<N;++i) ++cntrk[rk1[i]]; 33 for( i=1;i<N;++i) cntrk[i]+=cntrk[i-1]; 34 for( i=N-1;i>=0;--i) sa[--cntrk[rk1[tmpsa[i]]]]=tmpsa[i]; 35 rk[sa[0]]=0; 36 flg=true; 37 for( i=1;i<N;++i){ 38 if(rk1[sa[i]]==rk1[sa[i-1]] && rk2[sa[i]]==rk2[sa[i-1]]) rk[sa[i]]=rk[sa[i-1]],flg=false; 39 else rk[sa[i]]=rk[sa[i-1]]+1; 40 } 41 if(flg) break; 42 } 43 } 44 void gthght(){ 45 int l=0; 46 for(int i=0;i<N;++i)if(rk[i]){ 47 int j=sa[rk[i]-1]; 48 while(i+l<N && j+l<N && s[i+l]==s[j+l]) ++l; 49 hght[rk[i]]=l; 50 l-=(l>0); 51 } 52 } 53 long long ans=0; 54 void gtans(int x){ 55 if(!x) return ; 56 int r=gttp(x),l=gttp(x-1); 57 ans+=(long long)(ll[r]*rr[l]+rr[r]*ll[l])*hght[x]; 58 tp[l]=r,ll[r]+=ll[l],rr[r]+=rr[l]; 59 } 60 int a[410000]; 61 bool cmp(int x,int y){ return hght[x]>hght[y];} 62 int main(){//freopen("ddd.in","r",stdin); 63 scanf("%s",s); n=strlen(s); s[n]='$'; 64 scanf("%s",s+n+1); N=strlen(s); s[N++]='#'; 65 gtsffxrk(),gthght(); 66 for(int i=0;i<N;++i){ 67 a[i]=tp[i]=i; 68 ll[i]=(sa[i]<n); 69 rr[i]=ll[i]^1; 70 } 71 sort(a,a+N,cmp); 72 for(int i=0;i<N;++i) gtans(a[i]); 73 cout<<ans<<endl; 74 return 0; 75 }