生活随笔
收集整理的這篇文章主要介紹了
[bzoj4566][HAOI2016]找相同字符
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
4566: [Haoi2016]找相同字符
Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 113 Solved: 64
[Submit][Status][Discuss]
Description
給定兩個字符串,求出在兩個字符串中各取出一個子串使得這兩個子串相同的方案數。兩個方案不同當且僅當這兩
個子串中有一個位置不同。
Input
兩行,兩個字符串s1,s2,長度分別為n1,n2。1 <=n1, n2<= 200000,字符串中只有小寫字母
Output
輸出一個整數表示答案
Sample Input
aabb
bbaa
Sample Output
10
把兩個串用一個很大的字符連接起來,求一個后綴數組。
考慮怎樣暴力的算答案。
在 rank 數組中從前往后枚舉起點,對于每個枚舉的起點,都暴力的往后掃,掃的過程中維護一個 height 的最小值。每到一個點的時候,如果這個點跟起點不屬于一個串,就將答案加上當前的最小值,這樣是 O(n2) 的(注釋的代碼就是。。)
考慮這個還能怎么算。可以發現我們是維護 height 的最小值。那么我們可以按照 height 從大到小的順序掃,這樣每次需要用的就是當前的 height 。
掃的過程中用并查集維護一下每個串分別對哪些串有貢獻的(也就是 height 數組的貢獻)。
用乘法原理算一下當前的 height 會有多少貢獻。就是用當前的 height 乘上這個串和上一個串分別對于兩個兩個不同的原串的乘積的和。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int N=
400010;
LL ans;
char ss[N];
int n,m,len[
2],sa[N],c[N],rank[N],height[N],t1[N],t2[N],s[N],fa[N],st[N],en[N],a[N];
inline bool cmp(
int *y,
int p,
int q,
int k){
int o0=p+k>=n?-
1:y[p+k];
int o1=q+k>=n?-
1:y[q+k];
return o0==o1&&y[p]==y[q];
}
inline void build_sa(){
int i,k,p,*x=t1,*y=t2;
for(m=
28,i=
0;i<m;++i) c[i]=
0;
for(i=
0;i<n;++i) ++c[x[i]=s[i]];
for(i=
1;i<m;++i) c[i]+=c[i-
1];
for(i=n-
1;~i;--i) sa[--c[x[i]]]=i;
for(k=
1;k<=n;k<<=
1){
for(p=
0,i=n-k;i<n;++i) y[p++]=i;
for(i=
0;i<n;++i)
if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=
0;i<m;++i) c[i]=
0;
for(i=
0;i<n;++i) ++c[x[y[i]]];
for(i=
1;i<m;++i) c[i]+=c[i-
1];
for(i=n-
1;~i;--i) sa[--c[x[y[i]]]]=y[i];swap(x,y);m=
1;x[sa[
0]]=
0;
for(i=
1;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-
1],k)?m-
1:m++;
if(m>=n)
break;}
}
inline void build_height(){
int i,k=
0,j;
for(i=
0;i<n;++i) rank[sa[i]]=i;
for(i=
0;i<n;++i){
if(!rank[i])
continue;k=k?--k:k;j=sa[rank[i]-
1];
while(s[i+k]==s[j+k]) ++k;height[rank[i]]=k;}
}
inline bool CMP(
int x,
int y){
return height[x]>height[y];
}
inline int find(
int x){
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}
inline void calc(
int x){
if(!x)
return ;
int r1=find(x),r2=find(x-
1);ans+=(LL)(st[r1]*en[r2]+st[r2]*en[r1])*(LL)height[x];st[r1]+=st[r2];en[r1]+=en[r2];fa[r2]=r1;
}
int main(){
int i,j,minn;
scanf(
"%s",ss);len[
0]=
strlen(ss);
for(i=
0;i<len[
0];++i) s[i]=ss[i]-
'a'+
1;
scanf(
"%s",ss);len[
1]=
strlen(ss);
for(s[len[
0]]=
27,i=
0;i<len[
1];++i) s[i+len[
0]+
1]=ss[i]-
'a'+
1;n=len[
0]+len[
1]+
1;build_sa();build_height();
for(i=
0;i<n;++i){a[i]=fa[i]=i;st[i]=(sa[i]<len[
0]);en[i]=
1-st[i];}sort(a,a+n,CMP);
for(i=
0;i<n;++i) calc(a[i]);
printf(
"%lld\n",ans);
}
總結
以上是生活随笔為你收集整理的[bzoj4566][HAOI2016]找相同字符的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。