知识点:字符串哈希,二分
看数据的范围是1e6,目前字符串算法只学了哈希,就用哈希来做,哈希一般都是陪着二分一起来用的,用的方法很简单,就是按照顺序遍历子串,我们可以得到当前子串的出现的次数,
如果大于已经记录的答案,那么更新答案和位置,我们知道位置,知道子串的长度,那么就相当于知道这个子串的哈希值,就可以来比较了,
如果等于已经记录的答案,那么说明这个子串和记录的子串不同,我们通过二分来比较两个字符串的大小,这里应该是二分+字符串哈希的关键所在,当比较字典序的两个字符串比较长的时候,我们需要用二分而不是比较函数,这里选择最大化的二分,也就是二分两个字符串相同的长度,那么范围很显然是0到n-1,因为能进入这个分支已经说明两个字符串不同,然后我们再比较第一个不相同的位置的字符的大小就行了,如果当前的字符串更小,那么记录答案并且更新就行了,最后输出答案就行了,
总结,字符串哈希我们通常使用的就是进制哈希,如果用它来解决高级算法解决的问题的时候,通常时间复杂度多一个log,勉强能过,通常搭配二分一起使用,这里二分就是加快比较两个非常长的字符串的字典序的速度,同时也要牢记进制哈希的基本操作,初始化和查找哈希值,通过类比前缀和,我们查找哈希值的操作一定要通过相减操作来完成,不要有一些奇奇怪怪的操作,比如取模之类的就会错了
- #include
-
- 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
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;
- }