1. 程式人生 > 實用技巧 >BZOJ-2081 [Poi2010]Beads(hash)

BZOJ-2081 [Poi2010]Beads(hash)

題目描述

  把序列 \(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;
}