题意:给出一个字符串。求有多少个子串,长度为M*L,从前到后,分成M个长度为L的子串,每个子串都不相同。
思路:字符串hash。
因为起始位置有L种可能,我们要分别统计。利用hash后的值,我们可以判断子串是否重复。
首先,我先想到的是记录每个子串在L中情况下,离他最近的且相同的子串的位置,如果位置大于M*L,那就可以继续扩展一个长度为L的子串,当长度达到M*L的时候,我们就可以答案加1.
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int MAGIC = 233;
const int MAX = 100010;
unsigned int h[MAX];
unsigned int base[MAX];
int cnt[MAX];
map<unsigned int,int> ma[MAX];
char s[MAX];
int M,L;
inline void init_hash(char *s, unsigned int *h, int l)
{
h[0]= 0;
for(int i = 1; i <= l; ++i)
h[i] = h[i-1] * MAGIC + s[i-1];
base[0] = 1;
for(int i = 1; i <= l; ++i)
base[i] = base[i-1] * MAGIC;
}
inline unsigned int string_hash(unsigned *h, int l, int r)
{
return h[r] - h[l]* base[r-l];
}
int main(void)
{
//freopen("input.txt","r",stdin);
while(scanf("%d %d", &M, &L) != EOF)
{
scanf("%s",s);
int n = strlen(s);
int ans = 0;
init_hash(s,h,n);
memset(cnt,0,sizeof(cnt));
for(int i = 0; i < L; ++i)
ma[i].clear();
for(int i = 0; i < L; ++i)
cnt[i] = 0;
for(int i = L; i <= n; ++i){
unsigned ha = string_hash(h,i-L,i);
int id = i % L;
if(!ma[id].count(ha))
{
cnt[i] = cnt[i-L] + L;
if(cnt[i] >= M * L){
ans++;
cnt[i] = M * L;
}
}
else
{
if((i - ma[id][ha] > cnt[i-L]))
{
cnt[i] = cnt[i-L] + L;
if(cnt[i] >= M * L){
ans++;
cnt[i] = M * L;
}
}
else
cnt[i] = i - ma[id][ha];
}
ma[id][ha] = i;
}
printf("%d\n",ans);
}
return 0;
}
但是不用这么麻烦的。map既可以用来计数,也可用来判重。这样,我们直接利用每种情况下map的size的大小就行了。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int MAGIC = 233;
const int MAX = 100010;
unsigned int h[MAX];
unsigned int base[MAX];
map<unsigned int,int> ma[MAX];
char s[MAX];
int M,L;
inline void init_hash(char *s, unsigned int *h, int l)
{
h[0]= 0;
for(int i = 1; i <= l; ++i)
h[i] = h[i-1] * MAGIC + s[i-1];
base[0] = 1;
for(int i = 1; i <= l; ++i)
base[i] = base[i-1] * MAGIC;
}
inline unsigned int string_hash(unsigned *h, int l, int r)
{
return h[r] - h[l]* base[r-l];
}
int main(void)
{
//freopen("input.txt","r",stdin);
while(scanf("%d %d", &M, &L) != EOF)
{
scanf("%s",s);
int n = strlen(s);
int ans = 0;
init_hash(s,h,n);
for(int i = 0; i < L; ++i)
ma[i].clear();
for(int i = L; i <= n; ++i){
unsigned ha = string_hash(h,i-L,i);
int id = i % L;
ma[id][ha]++;
if(ma[id].size() == M) ans++;
if(i >= M * L){
int st = i - M * L;
unsigned ha1 = string_hash(h,st,st+L);
if(--ma[id][ha1] == 0) ma[id].erase(ha1);
}
}
printf("%d\n",ans);
}
return 0;
}
注意:也给自己提个醒吧,在上面的hash函数中,需要hash的字符串为左闭右开区间。切记,切记。