题目链接: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;
}