[bzoj4566][HAOI2016]找相同字符(后缀数组)
生活随笔
收集整理的這篇文章主要介紹了
[bzoj4566][HAOI2016]找相同字符(后缀数组)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目
傳送門
題解
這里:把兩個串用一個很大的字符連接起來,求一個后綴數組。
考慮怎樣暴力的算答案。
在 rank? r a n k 數組中從前往后枚舉起點,對于每個枚舉的起點,都暴力的往后掃,掃的過程中維護一個 height? h e i g h t 的最小值。每到一個點的時候,如果這個點跟起點不屬于一個串,就將答案加上當前的最小值,這樣是O(n2)的考慮這個還能怎么算。可以發現我們是維護 height? h e i g h t 的最小值。那么我們可以按照 height? h e i g h t 從大到小的順序掃,這樣每次需要用的就是當前的 height? h e i g h t 。
掃的過程中用并查集維護一下每個串分別對哪些串有貢獻的(也就是 height? h e i g h t 數組的貢獻)。
用乘法原理算一下當前的 height? h e i g h t 會有多少貢獻。就是用當前的 height? h e i g h t 乘上這個串和上一個串分別對于兩個兩個不同的原串的乘積的和。
代碼
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define ll long long const int maxn=1e6; const int inf=1e9;int n,len1,m=200,x[maxn],y[maxn],c[maxn],sa[maxn],rnk[maxn],height[maxn],fa[maxn],id[maxn]; int st[maxn],ed[maxn]; char s[maxn],ch[maxn]; ll ans;void build_sa() {for (int i=0; i<m; i++) c[i]=0;for (int i=0; i<n; i++) c[x[i]=s[i]]++;for (int i=1; i<m; i++) c[i]+=c[i-1];for (int i=n-1; i>=0; i--) sa[--c[x[i]]]=i;for (int k=1; k<=n; k<<=1){int p=0;for (int i=n-k; i<n; i++) y[p++]=i;for (int i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;for (int i=0; i<m; i++) c[i]=0;for (int i=0; i<n; i++) c[x[i]]++;for (int i=1; i<m; i++) c[i]+=c[i-1];for (int i=n-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];swap(x,y);p=1; x[sa[0]]=0;for (int i=0; i<n; i++)x[sa[i]] = y[sa[i-1]]==y[sa[i]] && ((sa[i-1]+k>=n?-1:y[sa[i-1]+k])==(sa[i]+k>=n?-1:y[sa[i]+k])) ?p-1:p++;if (p>n) break;m=p;} }void build_height() {int k=0;for (int i=0; i<n; i++) rnk[sa[i]]=i;for (int i=0; i<n; i++){if (!rnk[i]) continue;if (k) k--;int j=sa[rnk[i]-1];while (i+k<n && j+k<n && s[i+k]==s[j+k]) k++;height[rnk[i]]=k;} }bool cmp(int x,int y) {return height[x]>height[y];} int find(int x) {if (fa[x]!=x) return fa[x]=find(fa[x]);return fa[x]; } void work(int x) {int xx=find(x); int yy=find(x-1);ans+=(ll)(st[xx]*ed[yy]+st[yy]*ed[xx])* (ll)height[x]; // printf("r1 %d r2 %d :(%d*%d + %d*%d) * %d\n",xx,yy,st[xx],ed[yy],st[yy],ed[xx],height[x]);st[xx]+=st[yy];ed[xx]+=ed[yy]; // printf("%d %d\nans:%d\n",st[xx],ed[xx],ans);fa[yy]=xx; }int main() {scanf("%s%s",s,ch);len1=strlen(s); n=len1+strlen(ch); n++;s[len1]='#';for (int i=0; i<strlen(ch); i++)s[len1+1+i]=ch[i];build_sa();build_height();for (int i=0; i<n; i++) id[i]=i;for (int i=0; i<n; i++) fa[i]=i;for (int i=0; i<n; i++){st[i]=sa[i]<len1;ed[i]=st[i]^1;}sort(id,id+n,cmp);for (int i=0; i<n; i++) work(id[i]);printf("%lld",ans);return 0; }總結
以上是生活随笔為你收集整理的[bzoj4566][HAOI2016]找相同字符(后缀数组)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 05_I.MX6ULL工程管理与蜂鸣器实
- 下一篇: 古筝入门教程:关于古筝的历史·构造·保养