题意:求一个字符串s的所有长度为n的子串,其中原串的不同字符个数为nc.
刚开始学哈希,一直都是套的模板,用的has都是比较大的161,233等,然后都是用map用来对应的,然后一做这题就卡了,一直re或者tle。终于翻了多篇博客之后,对这题以及哈希有了更深刻的理解
思路:首先好像做题的时候忽略了一行文字,大概意思就是nc*n不超过16,000,000,这也就是之前不懂为什么是采用的nc进制,因为题目既然告诉你不同的字母只有m种,那么你就类比于10进制的方法(因为十进制不同的数不就只有10种嘛),对于字符串中出现的没有一个字母对它进行一个赋值操作,用数字作为它的键值,同时这也是为了减少数据规模。
然后这题还有个坑点就是卡map,所以你就只能自己去开一个1,000,000大小的数组,然后存储得到的哈希值。
先贴一份ac慢速(94ms)代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
int n, m;
const int maxn = 16000001;
char a[maxn];
bool mp[maxn];
int name[300];
int has;
ull p, h;
int main() {
scanf("%d%d", &n, &m);
scanf("%s", a + 1);
h = 0, p = 1;
has = m;
int len = strlen(a + 1);
int k = 0;
for(int i = 1; i <= len; i++) {
if(name[a[i]] == 0) name[a[i]] = ++k;
if(k == m) break;
}
int cnt = 0;
for(int i = 1; i + n - 1 <= len; i++) {
ull sum = 0;
for(int j = i; j <= i + n - 1; j++) {
// sum = sum * has + a[j];
sum=sum*has+name[a[j]];
}
if(!mp[sum]) {
mp[sum] = 1;
cnt++;
}
}
printf("%d\n", cnt);
return 0;
}
再贴一份32ms的
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
int n, m;
const int maxn = 16000001;
char a[maxn];
bool mp[maxn];
int name[300];
int has;
ull p, h;
int main() {
scanf("%d%d", &n, &m);
scanf("%s", a + 1);
h = 0, p = 1;
has = m;
int len = strlen(a + 1);
int k = 0;
for(int i = 1; i <= len; i++) {
if(name[a[i]] == 0) name[a[i]] = ++k;
if(k == m) break;
}
int cnt = 0;
ull sum = 0;
for(int i = 1; i <= n; i++) {
p *= has;
sum = sum * has + name[a[i]];
}
cnt++;
mp[sum] = 1;
for(int i = 1 + n; i <= len; i++) {
// for(int j = i; j <= i + n - 1; j++) {
sum = sum * has + a[j];
// sum = sum * has + name[a[j]];
// }
sum = sum * has + name[a[i]] - p * name[a[i - n]];
if(!mp[sum]) {
mp[sum] = 1;
cnt++;
}
}
printf("%d\n", cnt);
return 0;
}