您的位置:首页 > 其它

BZOJ 2081 Beads Hash

2017-09-24 20:05 197 查看
题目描述

Zxl有一次决定制造一条项链,她以非常便宜的价格买了一长条鲜艳的珊瑚珠子,她现在也有一个机器,能把这条珠子切成很多块(子串),每块有k(k>0)个珠子,如果这条珠子的长度不是k的倍数,最后一块小于k的就不要拉(nc真浪费),保证珠子的长度为正整数。 Zxl喜欢多样的项链,为她应该怎样选择数字k来尽可能得到更多的不同的子串感到好奇,子串都是可以反转的,换句话说,子串(1,2,3)和(3,2,1)是一样的。写一个程序,为Zxl决定最适合的k从而获得最多不同的子串。
例如:这一串珠子是: (1,1,1,2,2,2,3,3,3,1,2,3,3,1,2,2,1,3,3,2,1), k=1的时候,我们得到3个不同的子串: (1),(2),(3) k=2的时候,我们得到6个不同的子串: (1,1),(1,2),(2,2),(3,3),(3,1),(2,3) k=3的时候,我们得到5个不同的子串: (1,1,1),(2,2,2),(3,3,3),(1,2,3),(3,1,2) k=4的时候,我们得到5个不同的子串: (1,1,1,2),(2,2,3,3),(3,1,2,3),(3,1,2,2),(1,3,3,2)


输入

共有两行,第一行一个整数n代表珠子的长度,(n<=200000),第二行是由空格分开的颜色ai(1<=ai<=n)。

输出

也有两行,第一行两个整数,第一个整数代表能获得的最大不同的子串个数,第二个整数代表能获得最大值的k的个数,第二行输出所有的k(中间有空格)。

样例输入

21

1 1 1 2 2 2 3 3 3 1 2 3 3 1 2 2 1 3 3 2 1


样例输出

6 1

2

题解:题目的本质就是求当把字符串分割成相同长度的子串时,最多有多少个不同的子串。既然是判断字符串是否相同,那么常规做法就是Hash,由于正反顺序视为相同,那就正在倒着各做一遍Hash,将正反两个Hash值相乘得到一个新的Hash值,只要判断两个串的新Hash值是否相同即可。可恶的是我开始竟然wa了,居然是Hash取数取小了。

总结:Hash针对字符串是个强有力的工具,把Hash用好,就可以用其得到大部分甚至全部的分,但Hash取数的时候一定要考虑清楚(不能太大(超数据上线unsigned long long),不能太小(不同串的Hash值相同)),所以以后如果题目中有Bug,在检查完所有的步骤后,在确认Hash取值合理,(可以尝试多个Hash取值看结果是否相同)。

//心无挂碍 自得澄清
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>
#define N 200060
#define ULL unsigned long long
using namespace std;
map<ULL,bool> q;
int n,date
;
int ans=0,num=0,cnt=0;
int print
;
struct node
{
ULL tmp
,Hash_left
,Hash_right
;
ULL make_Hash(int l,int r)
{
return (Hash_left[r]-Hash_left[l-1]*tmp[r-l+1])*(Hash_right[l]-Hash_right[r+1]*(tmp[r-l+1]));
}
}opt;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&date[i]);
opt.tmp[0]=1;
for(int i=1;i<=n;i++) opt.Hash_left[i]=opt.Hash_left[i-1]*532+date[i],opt.tmp[i]=opt.tmp[i-1]*532;
for(int i=n;i;i--) opt.Hash_right[i]=opt.Hash_right[i+1]*532+date[i];
for(int i=1;i<=n;i++)
{
q.clear();cnt=0;
for(int j=1;j+i-1<=n;j+=i)//不是j++,不要被习惯左右;
{
if(!q[opt.make_Hash(j,j+i-1)])
q[opt.make_Hash(j,j+i-1)]=1,cnt++;
}
if(ans==cnt) print[++num]=i;
else if(cnt>ans) print[num=1]=i,ans=cnt;
}
printf("%d %d\n",ans,num);
for(int i=1;i<num;i++) printf("%d ",print[i]);
printf("%d",print[num]);
return 0;
}
//当你战胜了苦难,他就是你的财富;当苦难战胜了你,他就是你的屈辱。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: