[POI2010]KOR-Beads(Hash)

传送门

分析

找不同的子串->字符串Hash
这里因为子串可以反转,所以前缀后缀Hash值。
在找是否重复时不能直接用循环,会超时。
有2种方法:set和Hash表。
因为set是STL中的函数,所以我用的是Hash表来实现。
这里要注意一点,在初始化Hash表时不用memset,只需要初始化上次用过的即可。(详见代码)
输出k用vector统计。

代码

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define full(a,b) memset(a,b,sizeof a)
#define ll long long
#define ull unsigned ll
int read()//快读 
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x*f;
}
#define debug 1
#define N 200005
#define b 13131
#define H 999979 //用一个较大的质数当下标 
int n,arr[N],cnt,mx;
ull f[N],g[N],p[N];
int head[H],nxt[H],ecnt;
ull to[H];
int csh[N],sum;//用来统计上次用的Hash表下标 
vector<int> v;
ull hh(int S,int E,int flag)//求字符串的区间Hash值 
{
	if(!flag) return f[E]-f[S-1]*p[E-S+1];
	return g[S]-g[E+1]*p[E-S+1];
}
void add(int h,ull u)//增加一个结点 
{
	to[++ecnt]=u;
	nxt[ecnt]=head[h];
	head[h]=ecnt;
	csh[++sum]=h;
}
int F(ull key)//Hash函数 
{
	return key%H;
}
int query(ull x)//询问 
{
	int h=F(x);
	for(int i=head[h]; i!=-1; i=nxt[i])
		if(to[i]==x) return 1;
	return 0;
}
int main()
{
	if(debug==-1)
	{
		freopen("Beads.in","r",stdin);
		freopen("Beads.out","w",stdout);
	}
	p[0]=1;
	for(int i=1; i<N; i++)
		p[i]=p[i-1]*b;//初始化 
	n=read();
	for(int i=1; i<=n; i++)
		arr[i]=read();
	f[1]=arr[1];
	for(int i=2; i<=n; i++)
		f[i]=f[i-1]*b+arr[i];
	g[n]=arr[n];
	for(int i=n-1; i>=1; i--)
		g[i]=g[i+1]*b+arr[i];//前缀后缀 
	full(head,-1);
	for(int k=1; k<=n; k++)
	{
		cnt=ecnt=0;
		for(int i=1; i<=sum; i++)
			head[csh[i]]=-1;//只初始化上次用了的 
		sum=0;
		for(int i=1; i+k-1<=n; i+=k)
		{
			ull t1=hh(i,i+k-1,0),t2=hh(i,i+k-1,1);
			if(query(t1)) continue;
			add(F(t1),t1);add(F(t2),t2);//插入前缀,后缀Hash值 
			++cnt;
		}
		if(cnt>mx) //有比原来更大的答案 
		{
			mx=cnt;//更新最大值 
			v.clear();
			v.push_back(k);
		}
		else if(cnt==mx) v.push_back(k);//是最大值,插入k 
	}
	printf("%d %d\n",mx,v.size());
	for(int i=0,sz=v.size(); i<sz; i++)
		printf("%d ",v[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值