HDU4821-String
题意
给一个字符串\(s\),问满足下列两个条件的子串有多少个。
1、子串的长度为\(N*M\)(表示其由\(M\)个长度为\(N\)的字符串拼成)
2、这\(M\)个长度为\(N\)的字符串必须两两互不相同(两个字符串任意一个位置上的字符不相同即可)
题解
每个字符串的长度给定为\(L\),想到\(Hash(l~r)=Hash[r]-Hash[l-1]*sum[N]\)。对于以\(i\)为起点的长为\(N*M\)的子串,把他的\(M\)个长度为\(N\)的小子串的哈希值均放入\(map\)中,如果\(map.size()==M\),那么说明他们互不相同。
如果枚举每个起点,会造成TLE。我们可以使用尺取法。对于已经找到的一个子串,把他的最前面的小子串去掉,在最后再加上一个长为\(N\)的小子串,不断循环,可以得到一系列的子串。那么可以将原串分成\(N\)个系列,从\(1\)到\(N\)枚举起点,不断生成,就可以得到所有的子串。
注
1、Hash值开long long
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int base=131;
const int mod=1e9+7;
const int N=1e5+10;
int M,L;
char s[N];
ll Hash[N];
int len;
ll sum,ans;
map<ll,int> m;
//Hash开longlong
void getHash(char a[]){
for(int i=1;i<=len;i++){
Hash[i]=(Hash[i-1]*base%mod+s[i])%mod;
}
}
void init(){
sum=1;
for(int i=1;i<=L;i++) sum=(sum*base)%mod;
}
void solve(int st){
m.clear();
int e=st+M*L-1;
if(e>len) return;
int l=st,r=st+L-1;
for(int i=1;i<=M;i++){
ll now=(Hash[r]-Hash[l-1]*sum%mod+mod)%mod;
m[now]++;
l+=L;r+=L;
}
if(m.size()==M) ans++;
while(e+L<=len){
e+=L;
l=st;r=l+L-1;
ll now=(Hash[r]-Hash[l-1]*sum%mod+mod)%mod;
m[now]--;
if(m[now]==0) m.erase(now);
st+=L;
r=e;l=r-L+1;
now=(Hash[r]-Hash[l-1]*sum%mod+mod)%mod;
m[now]++;
if(m.size()==M) ans++;
}
}
int main(){
while(scanf("%d%d",&M,&L)!=EOF){
ans=0;
scanf("%s",s+1);
len=strlen(s+1);
getHash(s);
init();
for(int i=1;i<=L;i++){
solve(i);
}
printf("%lld\n",ans);
}
return 0;
}