题意:求有多少个子串满足条件:
1. 长度为n*len
2. 每个长度为len的小子串不能相同
hash方法 设一个种子base 打表出nbase[i]表示base的i次方
从S最后一个字符开始打表 hash[i]=hash[i+1]*base+str[i]-'a'+1 即将i位以后的串hash成一个unsigned long long
然后每个len长度的小串的hash值即为 hash[i]-hash[i+len]*nbase[len]
base[0] = 1 ;
for(int i = 1 ; i <= maxn ; i++)
base[i] = base[i-1] * bs ;
这个等价于 每次 MOD 1<<32
枚举0 - len 位作为起点,
i位为起点时 n个串为 :
[ i , i+len) , [i+len , i+2*len) , ...[i+(n-1)*len , i+n*len ) 为一组
这个 时候继续搞下去....
去掉[ i , i+len) , 加上[i+n*len , i+(n+1)*len )
然后再去掉[i+len ,i+2*len) , 加上[i+(n+1)*len , i+(n+2)*len )
然后就搞完了, 其实这么搞是很快的
hash 部分, a-z 26个字母,相当于1-26 , 还有个0 。 所以 bs取27即可。
typedef unsigned long long ULL ;
const int maxn = 100000 ;
ULL base[maxn+8] ;
const ULL bs = (ULL)27 ;
char s[maxn + 8] ;
map<ULL , int> mp ;
ULL Hash[maxn + 8] ;
int main(){
base[0] = 1 ;
for(int i = 1 ; i <= maxn ; i++)
base[i] = base[i-1] * bs ;
int i , j , n , len , slen , sum ;
ULL t ;
while(scanf("%d%d" , &n , &len) != EOF){
scanf("%s" ,s) ;
slen = strlen(s) ;
Hash[slen] = 0 ;
for(i = slen-1 ; i >= 0 ; i--)
Hash[i] = Hash[i+1]*bs + s[i] - 'a' + 1 ;
sum = 0 ;
for(i = 0 ; i < len && i+n*len <= slen ; i++){
mp.clear() ;
for(j = i ; j < i+n*len ; j += len){
t = Hash[j] - Hash[j+len]*base[len] ;
mp[t]++ ;
}
if(mp.size() == n) sum++ ;
for(j = i+n*len ; j + len <= slen ; j += len){
t = Hash[j-n*len] - Hash[j-(n-1)*len]*base[len] ;
mp[t]-- ;
if(mp[t] == 0) mp.erase(t) ;
t = Hash[j] - Hash[j+len]*base[len] ;
mp[t]++ ;
if(mp.size() == n) sum++ ;
}
}
printf("%d\n" , sum) ;
}
return 0 ;
}