题意: 给出 n n n个数,可将它们分为连续的若干个串,每个串有 k k k个数(长度不足 k k k则丢弃),如串 ( 1 , 1 , 1 , 2 , 2 , 2 , 3 , 3 , 3 , 1 , 2 , 3 , 3 , 1 , 2 , 2 , 1 , 3 , 3 , 2 , 1 ) (1,1,1,2,2,2,3,3,3,1,2,3,3,1,2,2,1,3,3,2,1) (1,1,1,2,2,2,3,3,3,1,2,3,3,1,2,2,1,3,3,2,1),当 k = 2 k=2 k=2时,我们得到 6 6 6个不同的子串: ( 1 , 1 ) , ( 1 , 2 ) , ( 2 , 2 ) , ( 3 , 3 ) , ( 3 , 1 ) , ( 2 , 3 ) (1,1),(1,2),(2,2),(3,3),(3,1),(2,3) (1,1),(1,2),(2,2),(3,3),(3,1),(2,3)。 求使得不同的串最多的 k k k值及串的个数。串可翻转,即子串 1 , 2 , 3 1,2,3 1,2,3和 3 , 2 , 1 3,2,1 3,2,1被认为是一样的。
我们预处理出前缀哈希和后缀哈希,这里我们自然溢出,对于枚举到的每一个k,我们求出每一段的前缀哈希和后缀哈希,然后将2者相乘用set判重。
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int c=2000001001;
int n,a[1001000],k;
ull hash1[1001000],mi[1001000],ans,res[1001000],maxn,hash2[1001000];
set<ull> s;
int main()
{
mi[0]=1;
cin>>n;
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
for(int i=1;i<=n;++i)
{
hash1[i]=hash1[i-1]*c+a[i];//前缀哈希
mi[i]=mi[i-1]*c;
}
for(int i=n;i>=1;--i)
hash2[i]=hash2[i+1]*c+a[i];//后缀哈希
for(int i=1;i<=n;++i)//枚举题目中的k
{
if(n/i<maxn)//对于当前的k如果可以分成的段数小于已经求出的最大值
break;
s.clear();
k=0;
for(int j=i;j<=n;j+=i)//枚举每一段
{
ull g1=hash1[j]-hash1[j-i]*mi[i];//这一段的前缀哈希
ull g2=hash2[j-i+1]-hash2[j+1]*mi[i];//这一段的后缀哈希
ull g3=g1*g2;//将2者相乘放入set里
if(s.count(g3))
continue;
s.insert(g3);
k++;
}
if(k>maxn)
{
ans=1;
maxn=k;
res[ans]=i;
}
else if(k==maxn)
{
ans++;
res[ans]=i;
}
}
cout<<maxn<<" "<<ans<<endl;
for(int i=1;i<=ans;++i)
printf("%d ",res[i]);
return 0;
}