Fork me on GitHub

HDU4821-String

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;
} 
posted @ 2020-03-15 16:44  qjy_73  阅读(178)  评论(0)    收藏  举报