题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4821
https://acm.njupt.edu.cn/problem/HDU4821
题意:从一个字符串中分割出M个长度为L的子字符串,并且这些子字符串不相同(Hash值不同).
解题思路:
一开始我是直接暴力,从i开始,尺取法每次截取M个长度为L的字段,结果超时了。
后来看到别人的题解,对for循环进行了修改,i从1遍历到L,然后在循环里再进行删除第一个L长度,在后面加上一个L长度,(删头添尾)直到不能再加L的时候。
至于如何比较M个子串Hash不同,直接用一个map类型或者set类型进行统计数目就行。注意map类型时,如果m[i]==0,记得要用m.erase(i)将i删除。
#include<iostream>
#include<cstdio>
#include<string>
#include<string.h>
#include<set>
#include<map>
using namespace std;
#define ll unsigned long long
const int mod=1e9+7;
const int p=31;
ll Hash[110000];
ll Pow[110000];
char s[110000];
map<ll,int> m;
int M,L;
int len;
int ans;
void get_hash()
{
Hash[1]=s[1]-'a'+1;
for(int i=2;i<=len;i++)
Hash[i]=(Hash[i-1]*p+(s[i]-'a'+1))%mod;
}
ll cmp_hash(int l,int r)
{
return (Hash[r]+mod-Hash[l-1]*Pow[r-l+1]%mod)%mod;
}
int main()
{
Pow[0]=1;
for(int i=1;i<=100000;i++)
Pow[i]=Pow[i-1]*p%mod;
while(scanf("%d%d",&M,&L)!=EOF)
{
m.clear();
memset(Hash,0,sizeof(Hash));
ans=0;
scanf("%s",s+1);
len=strlen(s+1);
get_hash();
for(int i=1;i<=L&&i<=len-M*L+1;i++)
{
m.clear();
int cnt=0;
for(int j=i;j<=i+M*L-1;j+=L)
{
m[cmp_hash(j,j+L-1)]++;
}
if(m.size()==M)
ans++;
for(int j=i+M*L;j+L-1<=len;j+=L)
{
ll t1=cmp_hash(j,j+L-1);
ll t2=cmp_hash(j-M*L,j-M*L+L-1);
m[t2]--;
m[t1]++;
if(m[t2]==0)
m.erase(t2);
if(m.size()==M)
ans++;
}
}
cout<<ans<<endl;
}
return 0;
}