题意:给两个整数L,M和一个字符串,问这个字符串有多少个满足要求的子串。要求有两个:①子串的长度为L*M
②把子串分为M个长度为L的串以后,这些串不能有两个完全一样的串。
思路:这题当年读了整场也没读懂啊(哭),今天也读了好一会儿才反应过来T^T,真是不愉快。。。读懂题以后就很水了,通过字符串hash,可以很轻松地求出任意一个长度为L的子串的hash值。枚举字符串起始位置,这个从0枚举到L-1,然后,在这个位置开始,每L个字符作为一块,首先将前M块插入到map中,同时维护存在相同字符串的个数,如果这个数量为0,那么显然是满足要求的。接下来,将这个区间向右移,删掉第1块,加入第M+1块,同样维护那个值,这样都枚举一遍就行了~
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
typedef unsigned long long Ull;
const int mx = 233;
const int maxn = 100000+10;
char str[maxn];
Ull H[maxn],x[maxn];
map<Ull,int>mp;
inline Ull getval(int s,int len)
{
return H[s] - H[s+len]*x[len];
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
x[0] = 1;
for(int i = 1;i < maxn;++i) x[i] = x[i-1]*mx;
int M,L;
while(~scanf("%d%d",&M,&L))
{
scanf("%s",str);
int n = strlen(str);
H[n] = 0;
for(int i = n - 1;i >= 0;--i)
H[i] = H[i+1]*mx + str[i] - 'a';
Ull tval;
int v,ans = 0;
for(int i = 0;i < L;++i)
{
if(i + M*L > n) break;
mp.clear();
int s = i, t = i + (M-1)*L;
int cnt = 0;
for(int j = s;j <= t; j += L)
{
tval = getval(j,L);
if(mp.find(tval) == mp.end())
mp[tval] = 1;
else
{
v = mp[tval];
if(v == 1) cnt++;
mp[tval]++;
}
}
if(cnt == 0) ans++;
while(t + 2*L <= n)
{
tval = getval(s,L);
v = mp[tval];
if(v > 2) mp[tval]--;
else if(v == 2) cnt--,mp[tval]--;
else mp.erase(tval);
s += L;
tval = getval(t+L,L);
if(mp.find(tval) == mp.end())
mp[tval] = 1;
else
{
v = mp[tval];
if(v == 1) cnt++;
mp[tval]++;
}
t += L;
if(cnt == 0) ans ++;
}
}
printf("%d\n",ans);
}
return 0;
}