题目链接:
https://odzkskevi.qnssl.com/0187fdccf71569bb074542c4463bea64?v=1508888740
题意:
给你一个整数n和一串字符串,求最长出现至少n次的子串。
输出最长子串长度和最后一个子串的第一个字符位置。
做法:
1.二分长度求右界。
2.judge里hash,用map记录次数
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 40000;
const int base = 131;
char str[maxn + 10];
ull hah[maxn + 10];
ull f[maxn + 10];
int n, len;
unordered_map<ull,int>mp; //开全局比放在judge里快600ms
int judge(int x) {
mp.clear();
int cmp = 0, pos = -1;
for(int i = 0; i < len; ++ i) {
if(i + x - 1 >= len) break;
ull tmp_hah = hah[i + x - 1] - f[x] * hah[i - 1];
int tmp_mp = ++mp[tmp_hah]; //此处赋值给int,不用查两遍,实测速度快了近200ms
if(tmp_mp >= n) pos = i;
}
return pos;
}
void init() {
memset(hah, 0, (len + 2)*sizeof(ull));
hah[0] = str[0];
for(int i = 1; i < len; ++ i)
hah[i] = hah[i - 1] * base + str[i];
}
int main() {
f[0] = 1;
for(int i = 1; i < maxn + 10; ++ i)
f[i] = f[i - 1] * base;
while(~scanf("%d", &n) && n) {
scanf("%s", str);
len = strlen(str);
init();
if(judge(1) == -1) {
puts("none");
continue;
}
int low = 1, high = len;
while(low < high) {
int mid = low + high + 1 >> 1;
if(judge(mid) > -1) low = mid;
else high = mid - 1;
}
int pos = judge(low); //可以开全局,少一次judge快近100ms
printf("%d %d\n", low, pos);
}
}