代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<map>
#define getHashval(n, l) hv[n] - hv[n+l] * nbase[l]
using namespace std;
typedef unsigned long long ULL;
const int base = 31;
ULL hv[100000+1];
ULL nbase[100000+1];
map<ULL, int> hashmap; // 类型为map,实际当作一个集合来使用
int main()
{
int m, l, count;
string s;
nbase[0] = 1;
for(int i=1; i<=100000; i++)
nbase[i] = nbase[i-1] * base;
while(scanf("%d%d", &m, &l) != EOF)
{
cin >> s;
count = 0;
int len = (int)s.length();
hv[len] = 0;
for(int i=len-1; i>=0; i--)
hv[i] = hv[i+1] * base + s.at(i);
// 窗口A,长度为m*l,在输入字符串上滑动,每次滑动1个字符,可以看到len-m*l+1个字串
int end = len-m*l;
for(int i=0; i<l && i<=end; i++)
{
hashmap.clear();//哈希值集合初始化:清空
// 将长度为m*l的串,切割为长度为l的m个子串,分别将各个子串的哈希值放入hashmap集合
// map中,map的key是子串的哈希值,map的值是一个计数,即相同哈希值出现n次的话则值为n
for(int j=i; j<i+m*l; j+=l)
hashmap[getHashval(j, l)]++;
if((int)hashmap.size() == m)
count++;
// 窗口B,长度为m*l,开始与窗口A重合,每次向右滑动l个字符,到窗口右边与字符右边对齐为止
// 每滑动一次B窗口,将最左边那长度l子串的哈希值移出hashmap集合,右边子串的哈希值添加到集合
int end2 = len-m*l-l;
for (int j=i; j<=end2; j+=l)
{
ULL temp = getHashval(j, l);
hashmap[temp]--;
if(hashmap[temp] == 0)
hashmap.erase(temp);
hashmap[getHashval(j+m*l, l)]++;
if((int)hashmap.size() == m)
count++;
}
}
printf("%d\n",count);
}
return 0;
}