题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4821
题意:给你一个字符串,让你找到有多少个长度为m*l的子串,由m个长度为l的不同的串构成的
解法:
hash一下之后,就直接暴力找就好了
但是暴力不是直接枚举串的起点,这样是要T的。
其实对于已经找到的一个子串,我们只需要除去他的最开头的那个小子串,加上它末尾后一个小子串,不断循
环下去,就可以得到一系列的子串
因此可以把原来的串分成l个系列,每一个系列中的子串,都是可以由第一个子串减去一个小子串,加上一个
新子串得到,由此降到了o(n)的复杂度
//HDU 4821 hash
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
const int maxn = 1e5+10;
const int seed = 31;
uLL h[maxn], base[maxn];
map <uLL, int> mp;
char s[maxn];
uLL string_hash(int l, int r){ ///熟练掌握字符串哈希的写法,有点类似前缀和的思想
return h[r]-h[l-1]*base[r-l+1];
}
int m, l;
int main()
{
base[0] = 1;
for(int i = 1; i < maxn; i++) base[i] = base[i-1]*seed; //每一位的权重
while(scanf("%d%d", &m, &l) != EOF)
{
scanf("%s", s+1);
int len = strlen(s+1);
h[0] = 0;
for(int i = 1; i <= len; i++) h[i] = h[i-1]*seed + s[i] - 'a';
int ans = 0;
for(int i = 1; i <= l && i+m*l<=len; i++){
mp.clear();
for(int j = i; j < i+m*l; j+=l){
uLL x = string_hash(j, j+l-1);
mp[x]++;
}
if(mp.size() == m) ans++;
for(int j = i+m*l; j+l-1<=len; j+=l){
uLL x = string_hash(j, j+l-1);
mp[x]++;
uLL y = string_hash(j-m*l,j-m*l+l-1);
mp[y]--;
if(mp[y] == 0) mp.erase(y);
if(mp.size()==m) ans++;
}
}
printf("%d\n", ans);
}
return 0;
}