1005 Programming Pattern

知识点:字符串哈希,二分

看数据的范围是1e6,目前字符串算法只学了哈希,就用哈希来做,哈希一般都是陪着二分一起来用的,用的方法很简单,就是按照顺序遍历子串,我们可以得到当前子串的出现的次数,

如果大于已经记录的答案,那么更新答案和位置,我们知道位置,知道子串的长度,那么就相当于知道这个子串的哈希值,就可以来比较了,

如果等于已经记录的答案,那么说明这个子串和记录的子串不同,我们通过二分来比较两个字符串的大小,这里应该是二分+字符串哈希的关键所在,当比较字典序的两个字符串比较长的时候,我们需要用二分而不是比较函数,这里选择最大化的二分,也就是二分两个字符串相同的长度,那么范围很显然是0到n-1,因为能进入这个分支已经说明两个字符串不同,然后我们再比较第一个不相同的位置的字符的大小就行了,如果当前的字符串更小,那么记录答案并且更新就行了,最后输出答案就行了,

总结,字符串哈希我们通常使用的就是进制哈希,如果用它来解决高级算法解决的问题的时候,通常时间复杂度多一个log,勉强能过,通常搭配二分一起使用,这里二分就是加快比较两个非常长的字符串的字典序的速度,同时也要牢记进制哈希的基本操作,初始化和查找哈希值,通过类比前缀和,我们查找哈希值的操作一定要通过相减操作来完成,不要有一些奇奇怪怪的操作,比如取模之类的就会错了

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;

const int N = 1050000;
const int P = 131;

int n;
ull p[N], h[N];
string s;
unordered_map<ull, int> mp;

void init(int len) {
	p[0] = 1;
	for (int i = 1; i <= len; i++) {
		p[i] = p[i - 1] * P;
		h[i] = h[i - 1] * P + s[i - 1];
	}
}

inline ull get(int l, int r) {
	return h[r] - h[l - 1] * p[r - l + 1];
}

int main() {
	scanf("%d", &n);
	getchar();
	getline(cin, s);
	int len = s.size();
	init(len);
	int Max = 0, pos;
	for (int i = 1; i <= len - n + 1; i++) {
		ull cur = get(i, i + n - 1);
		int cnt = ++mp[cur];
		if (cnt > Max) {
			Max = cnt;
			pos = i;
		} else if (cnt == Max) {
			int l = 0, r = n - 1;
			while (l < r) {
				int mid = (l + r + 1) >> 1;
				if (get(i, i + mid - 1) == get(pos, pos + mid - 1)) l = mid;
				else r = mid - 1;
			}
			if (s[i + l - 1] < s[pos - 1 + l]) pos = i;
		}
	}
	for (int i = 0; i < n; i++) {
		printf("%c", s[pos - 1 + i]);
	}
	printf(" %d", mp[get(pos, pos + n - 1)]);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值