题目
nodgd写了一篇文章,自认为这是一篇好文章。nodgd的文章由n个小写英文字母组成。文章的一个子串指的是文章中的一段连续的字母,子串的长度就是这一段的字母个数。nodgd在文章中用了排比、对偶、前后照应之类的手法,所以就有很多个子串是相同或者相近的。为了向大家证明这是一篇好文章,nodgd决定给自己的文章进行评分。nodgd首先确定了一个整数m,然后统计出文章中有多少个不相同的长度为m的子串,这个数量就是文章的评分。
然而,nodgd懒得老老实实计算这个评分了,就把任务丢给了你。
对于30%的数据,1≤m≤n≤200; 对于50%的数据,1≤m≤n≤2000; 对于另外20%的数据,1≤m≤50≤n≤200000; 对于100%的数据,1≤m≤n≤200000。
题解
把字符串每一位用一个数m(最好是质数)乘起来,然后取出一段时就用末位的值减去首位前一位*m的长度次方,得到的是就是它的hash值
要采用双哈希,用两个大的质数,比如1e9+7,998224353,或者一些奇怪而没有规律的数,防止被卡
然后就可以了
代码
#include <cstdio>
using namespace std;
const int lwq=100000007,hxh=99824353;
int n,m,ans;
char a[200005];
bool a1[lwq+6],a2[hxh+6];
long long h1[200005],h2[200005];
long long p1,p2;
int main(){
scanf("%d%d",&n,&m);
scanf("%s",a+1);
for (int i=1;i<=n;i++){
h1[i]=(h1[i-1]*181+a[i]-'a')%lwq;
h2[i]=(h2[i-1]*107+a[i]-'a')%hxh;
}
p1=p2=1;
for (int i=1;i<=m;i++)
p1=(p1*181)%lwq,p2=(p2*107)%hxh;
for (int i=m;i<=n;i++){
int d1=0,d2=0;
d1=((long long)h1[i]-h1[i-m]*p1)%lwq,
d2=((long long)h2[i]-h2[i-m]*p2)%hxh;
if (d1<0) d1+=lwq;
if (d2<0) d2+=hxh;
if (a1[d1]&&a2[d2]) continue;
a1[d1]=1;a2[d2]=1;ans++;
}
printf("%d",ans);
}