【bzoj2081】[Poi2010]Beads Hash

题目描述

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


题解

Hash

处理不同字符串问题,Hash是个好方法。

然而这道题要求反过来的字符串与原串算作同一种,所以Hash值也应相同。

一种比较简单且容易实现的方法是把它们正反做两次Hash,然后把它们乘起来作为新的Hash值,因为乘法满足交换率。

这里为了防止被卡取了两个底数进行Hash。

最终使用map判断就行了。

时间复杂度为$O(n\log n*\log n)=O(n\log^2n)$

#include <cstdio>
#include <map>
#define N 200010
using namespace std;
typedef unsigned long long ull;
struct data
{
	ull b[N] , h1[N] , h2[N];
	ull hash(int l , int r)
	{
		return (h1[r] - h1[l - 1] * b[r - l + 1]) * (h2[l] - h2[r + 1] * b[r - l + 1]);
	}
}t1 , t2;
map<pair<ull , ull> , bool> f;
int a[N] , sta[N] , top;
int main()
{
	int n , i , j , cnt , ans = 0;
	scanf("%d" , &n);
	t1.b[0] = t2.b[0] = 1;
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]);
	for(i = 1 ; i <= n ; i ++ ) t1.b[i] = t1.b[i - 1] * 233 , t2.b[i] = t2.b[i - 1] * 2333;
	for(i = 1 ; i <= n ; i ++ ) t1.h1[i] = t1.h1[i - 1] * 233 + a[i] , t2.h1[i] = t2.h1[i - 1] * 2333 + a[i];
	for(i = n ; i >= 1 ; i -- ) t1.h2[i] = t1.h2[i + 1] * 233 + a[i] , t2.h2[i] = t2.h2[i + 1] * 2333 + a[i];
	for(i = 1 ; i <= n ; i ++ )
	{
		f.clear() , cnt = 0;
		for(j = 1 ; j + i - 1 <= n ; j += i)
			if(!f[make_pair(t1.hash(j , j + i - 1) , t2.hash(j , j + i - 1))])
				f[make_pair(t1.hash(j , j + i - 1) , t2.hash(j , j + i - 1))] = 1 , cnt ++ ;
		if(cnt > ans) top = 1 , sta[top] = i , ans = cnt;
		else if(cnt == ans) sta[++top] = i;
	}
	printf("%d %d\n" , ans , top);
	for(i = 1 ; i < top ; i ++ ) printf("%d " , sta[i]);
	printf("%d" , sta[top]);
	return 0;
}

 

 

posted @ 2017-06-21 19:19  GXZlegend  阅读(278)  评论(0编辑  收藏  举报