P3498 [POI2010]KOR-Beads 题解

前言:

最近在做哈希的题,发现了这道好题,看题解里很多大佬的方法都很巧妙,自己就发一个较为朴素的方法吧。

题意:

题目传送门

给你一个序列,需要求出数 k k k,使划分的子串长度为 k k k 时,不同的子串数量最多。还要注意几件事:

  1. 子串可以反转,比如 ( 1 , 2 , 3 ) (1,2,3) (1,2,3) 看做与 ( 3 , 2 , 1 ) (3,2,1) (3,2,1) 相同。

  2. 如果不能正好划分完,剩下的部分不计算。

  3. k k k 可能有多个,这时要输出所有的 k k k,顺序任意。

最后输出两行,第一行两个数,表示最多的不同的子串数量和所有可能的 k k k 的数量。第二行为每一个 k k k

思路:

这道题肯定要用哈希的(题目标签),因为我们要判断子串是否相同,而如果每一次都去从头到尾匹配,时间复杂度很高,一定会 TLE 飞。但如果用哈希,就可以 O ( 1 ) O(1) O(1) 比较。

不过,考虑子串可以翻转,可以做一个后缀哈希,即从后往前哈希一次,求出的哈希值即为子串反转后的哈希值。

接下来就好写了,用一个 map 来记录某个子串是否出现过,没出现过则记录并统计,最后更新答案即可。

注意每一次循环结束之后 map 要清零(因为存的是这一次的,下一次就用不到了)。

代码:

请勿抄袭

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int base=10000007;
const int N=2e5;
int n,tot,cnt;
int a[N+10];
ull h1[N+10],h2[N+10],power[N+10];
map<ull,bool> mp;
int ans[N+10];
inline void init()
{
	power[0]=1;
	for(int i=1;i<=n;i++) power[i]=power[i-1]*base;
	for(int i=1;i<=n;i++) 
	{
		h1[i]=h1[i-1]*base+a[i];
	}
	for(int i=n;i>=1;i--)
	{
		h2[i]=h2[i+1]*base+a[i];
	}
}
inline ull get_hash(int l,int r,bool f)
{
	if(f==0) return h1[r]-h1[l-1]*power[r-l+1];
	else return h2[l]-h2[r+1]*power[r-l+1];
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	init();
	for(int i=1;i<=n;i++)
	{
		if(n/i<tot) break;
		int sum=0;
		for(int j=i;j<=n;j+=i)
		{
			int hash1=get_hash(j-i+1,j,0);
			int hash2=get_hash(j-i+1,j,1);
			if(!mp[hash1]&&!mp[hash2])
			{
				mp[hash1]=1;
				mp[hash2]=1;
				sum++;
			}
		}
		if(sum>tot)
		{
			tot=sum;
			cnt=1;
			ans[cnt]=i;
		}
		else if(sum==tot) ans[++cnt]=i;
		mp.clear();
	}

	printf("%d %d\n",tot,cnt);
	for(int i=1;i<=cnt;i++)
	{
		printf("%d ",ans[i]);
	}
	return 0;
}

写题解不易,点个赞呗。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值