题目大意:
就是现在对于给出的字符串S, 找出S中有多少个长度为M*L的子串, 满足组成M*L长度的字串是有M个不同的长度为L的串拼接起来的
大致思路:
明摆着的字符串Hash, 预处理Hash之后对于连续的长度为L的段进行枚举即可
枚举起点, 每次向后L的一段, 这样是个很常见的枚举技巧了...没什么难度
代码如下:
Result : Accepted Memory : 4144 KB Time : 546 ms
/*
* Author: Gatevin
* Created Time: 2015/4/9 13:47:23
* File Name: Rin_Tohsaka.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e)
#define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl
typedef unsigned long long ulint;
const ulint seed = 50009uLL;
map<ulint, int> V;
#define maxn 100010
int M, L;
char s[maxn];
ulint H[maxn], xp[maxn];
void initHash(char *s, int len)
{
H[0] = s[0] - 'a';
for(int i = 1; i < len; i++)
H[i] = H[i - 1]*seed + (ulint)(s[i] - 'a');
}
ulint askHash(int l, int r)
{
if(l == 0) return H[r];
else return H[r] - H[l - 1]*xp[r - l + 1];
}
int main()
{
xp[0] = 1;
for(int i = 1; i < maxn; i++)
xp[i] = xp[i - 1]*seed;
while(scanf("%d %d", &M, &L) != EOF)
{
scanf("%s", s);
int len = strlen(s);
initHash(s, len);
int cnt = 0;
int ans = 0;
for(int star = 0; star < L; star++)//枚举分组
{
V.clear();
cnt = 0;
for(int i = 0; i < M && star + (i + 1)*L - 1 < len; i++)
{
if(V[askHash(star + i*L, star + (i + 1)*L - 1)] == 0)
cnt++;
V[askHash(star + i*L, star + (i + 1)*L - 1)]++;
}
if(cnt == M) ans++;
int last = star;
int now = star + M*L;
while(now + L <= len)
{
if(V[askHash(last, last + L - 1)] == 1)
cnt--;
V[askHash(last, last + L - 1)]--;
if(V[askHash(now, now + L - 1)] == 0)
cnt++;
V[askHash(now, now + L - 1)]++;
if(cnt == M) ans++;
last += L;
now += L;
}
}
printf("%d\n", ans);
}
return 0;
}