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;
}
posted on 2020-11-18 20:53 DestinHistoire 阅读(107) 评论(0) 收藏 举报
浙公网安备 33010602011771号