BZOJ4566: [Haoi2016]找相同字符
BZOJ4566: [Haoi2016]找相同字符
Description
給定兩個字符串,求出在兩個字符串中各取出一個子串使得這兩個子串相同的方案數。 兩個方案不同當且僅當這兩個子串中有一個位置不同。Input
兩行,兩個字符串s1,s2,長度分別為n1,n2。
1 <=n1, n2<= 200000,字符串中只有小寫字母
Output
輸出一個整數表示答案
Sample Input
aabbbbaa
Sample Output
10題解Here!
?$O(n^3)$的大暴力:
首先,我們知道。如果連個串中各取一個極長的相同的子串(極長的意思就是這兩個串再向兩邊延伸就會不同或出界),則這個極長的串的所有子串也是相同的。
由于枚舉極長的串并求出其長度并不方便,所以我們可以枚舉這個極長的串的左端點,再求出其長度。
這樣就可以不重不漏找出所有的極長的串。
更圖方便的話,我們可以只考慮兩原串中某后綴的所有前綴,這可以補充不漏找出所有的子串。
但是由于我們要在兩個字符串中枚舉,同時找出子串的長度也是$O(n)$的,于是總復雜度為$O(n^3)$。
$O(n^2)$的優化大暴力:
?剛才說道了后綴的前綴,我們不由自主想到了后綴數組。
如果我們可以很快求出$A$串和$B$串的某兩個后綴的最長公共前綴,我們就可以將時間復雜度優化的更低。
這不就是$height$數組解決的嘛!
所以我們可以將兩個字符串通過一個分隔符拼接起來,求出$height$數組,即按字典序排序后每個后綴和前一個的$LCP$。
再利用$height$數組和$ST$表,我們就可以$O(1)$得到任意兩后綴的$LCP$。
因此,這樣做的時間復雜度只有枚舉子串起點的復雜度了,即$O(n^2)$。
$O(nlog_2n)$的正解:
?現在拉高時間復雜度的罪魁禍首就是枚舉起點了。
所以我們想將其復雜度降低。
考慮到利用$height$數組求任意兩個后綴的$LCP$時的獨特性質:
兩個后綴的$LCP$為字典序排序后他們中間夾的最小的$height$。
也就是說排序后,一個后綴越往后數$LCP$的長度越小。
這樣,我們就可以用單調棧維護這個最小值。
分$A$串的子串在前、$B$的子串在前兩種情況分別用單調棧求出答案,加起來就行。
至于如何維護,emmm......
? 由于利用了單調棧這個神奇的手段,時間復雜度降到了$O(nlog_2n)$,也就是倍增處理后綴數組的復雜度。
當然你可以用$DC3$來完成,復雜度可以降到$O(n)$。(當然,本蒟蒻不會啦。。。)
附代碼:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 400010
using namespace std;
pair<int,long long> stack[MAXN];
int n,l1,l2;
int val[MAXN],sum[MAXN];
char str[MAXN];
int top,sa[MAXN],rk[MAXN],tax[MAXN],tp[MAXN],height[MAXN];
inline int read(){int date=0,w=1;char c=0;while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}return date*w;
}
void radixsort(){for(int i=0;i<=top;i++)tax[i]=0;for(int i=1;i<=n;i++)tax[rk[i]]++;for(int i=1;i<=top;i++)tax[i]+=tax[i-1];for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffixsort(){top=30;for(int i=1;i<=n;i++){rk[i]=val[i];tp[i]=i;}radixsort();for(int w=1,p=0;p<n;top=p,w<<=1){p=0;for(int i=1;i<=w;i++)tp[++p]=n-w+i;for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;radixsort();swap(tp,rk);rk[sa[1]]=p=1;for(int i=2;i<=n;i++)rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;}
}
void getheight(){for(int i=1,j,k=0;i<=n;i++){if(k)k--;j=sa[rk[i]-1];while(val[i+k]==val[j+k])k++;height[rk[i]]=k;}
}
void work(){long long ans=0;top=0;stack[0]=make_pair(1,0);for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(sa[i]<=l1);for(int i=1;i<=n;i++){while(top&&height[i]<height[stack[top].first])top--;top++;stack[top]=make_pair(i,(long long)(sum[i-1]-sum[stack[top-1].first-1])*height[i]+stack[top-1].second);if(sa[i]>l1+1)ans+=stack[top].second;}top=0;for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(sa[i]>l1+1);for(int i=1;i<=n;i++){while(top&&height[i]<height[stack[top].first])top--;top++;stack[top]=make_pair(i,(long long)(sum[i-1]-sum[stack[top-1].first-1])*height[i]+stack[top-1].second);if(sa[i]<=l1+1)ans+=stack[top].second;}printf("%lld\n",ans);
}
void init(){scanf("%s",str+1);l1=n=strlen(str+1);str[++n]='z'+1;scanf("%s",str+n+1);n=strlen(str+1);for(int i=1;i<=n;i++)val[i]=str[i]-'a'+1;suffixsort();getheight();
}
int main(){init();work();return 0;
}
以上是后綴數組解法,當然你可以用$SAM$吊打$SA$,就像$AC$自動機吊打$Trie$和$KMP$一樣。。。
(坑,未填。。。)
轉載于:https://www.cnblogs.com/Yangrui-Blog/p/9451807.html
總結
以上是生活随笔為你收集整理的BZOJ4566: [Haoi2016]找相同字符的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 万能的天涯,帮我看看这是什么衣服牌子,进
- 下一篇: Linux命令:tar命令批量解压方法总