BZOJ 2081 Beads Hash

来源:互联网 发布:拓展人脉的软件 编辑:程序博客网 时间:2024/06/06 15:51

题目描述

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 longusing namespace std;map<ULL,bool> q;int n,date[N];int ans=0,num=0,cnt=0;int print[N];struct node{ULL tmp[N],Hash_left[N],Hash_right[N];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;}//当你战胜了苦难,他就是你的财富;当苦难战胜了你,他就是你的屈辱。 


原创粉丝点击