BZOJ-2081 [Poi2010]Beads(hash)
阿新 • • 發佈:2020-11-18
題目描述
把序列 \(a(1\leq |a|\leq 2\times 10^5)\) 從開頭開始切成長度為 \(k\) 的若干子串(若最後一段子串的長度小於 \(k\) 則捨棄),選擇一個最合適的 \(k\) 值,使不同子串的數量儘可能多(子串可以翻轉)。輸出最多不同子串的個數,能獲得最大值的 \(k\) 的個數。
分析
直接列舉 \(k\) 的大小,同時計算序列字尾的 \(\text{hash}\) 值,暴力判斷即可,時間複雜度 \(O(n+\frac{n}{2}+\frac{n}{3}+\cdots+1)\approx O(n\log n)\)。由於不同的 \(\text{hash}\)
set
存,所以時間複雜度會再帶一個 \(\log\),即 \(O(n\log^2 n)\)。在判斷某個子串是否是另一個子串的翻轉時,可以把字首 \(\text{hash}\) 值和字尾 \(\text{hash}\) 值的較小值插入到 set
中,即可達到去重的效果。
程式碼
#include<bits/stdc++.h> using namespace std; const int N=2e5+10; const int BASE1=131; const int BASE2=139; int n,a[N+10],temp[N+10]; unsigned long long pow1[N+10],pow2[N+10]; unsigned long long hash1[N+10],hash2[N+10]; unsigned long long __hash1[N+10],__hash2[N+10]; unsigned long long get1(int op,int l,int r) { if(op==1) return hash1[r]-hash1[l-1]*pow1[r-l+1]; if(op==2) return hash2[r]-hash2[l-1]*pow2[r-l+1]; } unsigned long long get2(int op,int l,int r) { if(op==1) return __hash1[l]-__hash1[r+1]*pow1[r-l+1]; if(op==2) return __hash2[l]-__hash2[r+1]*pow2[r-l+1]; } set<pair<unsigned long long,unsigned long long> > st; int num(int len) { st.clear(); for(int l=1;l+len-1<=n;l=l+len) { int r=l+len-1; unsigned long long X1=get1(1,l,r),X2=get1(2,l,r); unsigned long long Y1=get2(1,l,r),Y2=get2(2,l,r); st.insert(make_pair(min(X1,Y1),min(X2,Y2))); } int sz=st.size(); return sz; } int main() { cin>>n; for(int i=1;i<=n;i++) scanf("%d",&a[i]); pow1[0]=pow2[0]=1; for(int i=1;i<=n;i++) { pow1[i]=pow1[i-1]*BASE1; pow2[i]=pow2[i-1]*BASE2; } for(int i=1;i<=n;i++) { hash1[i]=hash1[i-1]*BASE1+a[i]; hash2[i]=hash2[i-1]*BASE2+a[i]; } for(int i=n;i>=1;i--) { __hash1[i]=__hash1[i+1]*BASE1+a[i]; __hash2[i]=__hash2[i+1]*BASE2+a[i]; } int maxn=-1,cnt=0; for(int i=1;i<=n;i++) { int ans=num(i); if(ans>maxn) { maxn=ans; cnt=0; } if(ans==maxn) temp[++cnt]=i; } cout<<maxn<<" "<<cnt<<endl; for(int i=1;i<=cnt;i++) printf("%d ",temp[i]); puts(""); return 0; }