题意:
给出一段字符串S(|s| <= 1e5),和两个整数M和L,(1<= M * L <= |s|)
求出长度为M*L,可分为长为L的M段,且每段都不相同的S的子串的个数
思路:
先对整个字符串的每个字符hash,使用STL中的map去映射每个不同的长度为L的子串的个数
长为L的子串的哈希值是Hash[j]-Hash[j+L]*B[L]。
先找到一个长度为M*L的串,再依次将首个L串去掉,增加一个新L串,判断是否合法(O(M*L))
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <queue>
#include <map>
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
typedef unsigned long long ull;
char s[maxn];
ull Hash[maxn];
ull B[maxn];
ull base = 31;
int m,l;
map<ull,int> ma;
void pre()
{
B[0] = 1;
for(int i = 1; i <= maxn - 10; i++){
B[i] = B[i - 1] * base;
}
}
void work()
{
scanf("%s",s);
int len = strlen(s);
Hash[len] = 0;
for(int i = len - 1; i >= 0; i--){
Hash[i] = Hash[i + 1] * base + s[i] - 'a' + 1;
}
int ans = 0;
for(int i = 0; i < l && i + m * l < len ; i++){
ma.clear();
for(int j = i; j < i + m * l; j += l){
ma[Hash[j] - Hash[j + l] * B[l]]++;
}
if(ma.size() == m)
ans++;
for(int j = i + l; j <= len - m * l; j += l){
ma[Hash[j-l] - Hash[j] * B[l]]--;
if(ma[Hash[j-l] - Hash[j] * B[l]] == 0)
ma.erase(Hash[j-l] - Hash[j] * B[l]);
ma[Hash[j + (m - 1) * l ] - Hash[j + m * l] * B[l]]++;
if(ma.size() == m)
ans++;
}
}
printf("%d\n",ans);
}
int main()
{
#ifdef LOCAL
freopen("case.in","r",stdin);
// freopen("case.out","w",stdout);
#endif
pre();
while(~scanf("%d%d",&m,&l))
work();
return 0;
}