本题目意思:
给定一个长度为1E5的串,给定N,M,让统计所有的连续子串,满足串长为N*M,而且这N个长为M的串两两不相同,输出个数。
分析:
本题目明显使用哈希来做的,因为在LONG LONG 内散列不超过 1E5个子串,出现冲突的概率极低。
然后不能从头暴力算出第一个长为N*M的串每个长M的哈希值,然后递推计算后面的每个长为N*M串的hash值,这样复杂度为(len - N*M)*(N*log(M))很容易被卡掉的,算一下就清楚。
那么,暴力的方法是:
记hash(1->m),为远串位置从1->m的子串的hash值,先计算出所有的 hash( k*m+1 , (k+1)*m),
那么第一个长为N*M的串的hash集合为上面的K 取 0 - > n-1; 那么 位置在 m+1 - > (n+1)*m的子串的哈希集合就是在前面的基础上在前面去掉一个,在后面加上一个,
那么用map维护hash集合的变动,每个这样的串的复杂度为o(log(m))
所以加上递推初始所有hash值,总复杂度 m*(len / m) + (len - n*m)*log(m),这样的复杂度组大不超过 len*log(M),所以可以ac;
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <cctype>
#define rep1(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
typedef long long LL;
const int base = 131;
const int N = 101010;
const LL mod = 1e16;
char str[N];
int n,m;
LL ha[N];
map<LL,int> M;
int main()
{
memset(str,0,sizeof(str));
while(scanf("%d %d",&n,&m)==2){
scanf("%s",str+1);
int len = strlen(str+1), all = len/m; LL tem = 0;
rep1(i,1,len){
tem=(tem*base+str[i])%mod; if(i%m == 0) {ha[i/m]=tem; tem=0;}
}
LL ba = 1; rep1(i,1,m-1) ba=ba*base%mod;
int res = 0;
rep1(i,1,m){
if(i + n*m -1 > len) break;
M.clear();
if(i > 1){
rep1(j,1,all){
ha[j]=(ha[j]-ba*str[(j-1)*m+i-1]+mod)%mod;
ha[j]=(ha[j]*base+str[j*m+i-1])%mod;
if(j<=n) M[ha[j]]++;
}
}
else rep1(j,1,n) M[ha[j]]++;
if(M.size() == n) res++;
for(int st = i+n*m-1+m,j=1; st<=len;st+=m,j++){
M[ha[j]]--; M[ha[j+n]]++;
if(M[ha[j]] == 0) M.erase(ha[j]);
if(M.size() == n) res++;
}
}
printf("%d\n",res);
}
return 0;
}