入门oj6489:文章评分(hash)
题目
Description
nodgd的文章由n个小写英文字母组成。文章的一个子串指的是文章中的一段连续的字母,子串的长度就是这一段的字母个数。nodgd在文章中用了排比、对偶、前后照应之类的手法,所以就有很多个子串是相同或者相近的。为了向大家证明这是一篇好文章,nodgd决定给自己的文章进行评分。nodgd 首先确定了一个整数m,然后统计出文章中有多少个不相同的长度为m的子串,这个数量就是文章的评分。
Input
第一行包含两个整数n,m,表示文章的长度和需要统计的子串长度。
1≤m≤n≤200000
第二行包含一个长度为n的只包含小写字母的字符串。
1≤m≤n≤200000
第二行包含一个长度为n的只包含小写字母的字符串。
Output
一行一个整数,表示文章的评分。
Sample Input
5 3
aaaab
Sample Output
2
【提示】
【样例解释1】
长度为3的子串有3个,分别是 aaa,aaa,aab,其中不同的只有2个。
HINT
思路:
一道很明显的$hash$题,我们可以用$map$来存每一个字串的$hash$值,再看有多少个不同的;
由于$oj$的数据强化了,所以要用双$hash$,但蒟蒻博主用双$hash$却和数据的答案有几个数的相差;
所以,博主用了三$hash$就过了;
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; inline ll read() { ll a=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*f; } const int mod=19260819,mod1=19260809,mod2=19260719;//防止数据过大的mod ll p[1000001],p1[1000001],p2[1000001],ha[1000001],ha1[1000001],ha2[1000001]; ll b=193,b1=137,b2=107;//每个hash值的进制 三hash ll n,m; map<ll,bool> a,a1,a2; char c[1000001]; int main() { //不懂的去网上看hash基础,或私聊博主 p[0]=1;p1[0]=1;p2[0]=1; for(ll i=1;i<=1000001;i++) p[i]=p[i-1]*b%mod, p1[i]=p1[i-1]*b1%mod1, p2[i]=p2[i-1]*b2%mod2;//记录每一位上转进制时要乘的数 n=read();m=read(); scanf("%s",(c+1)); for(ll i=1;i<=n;i++) ha[i]=(ha[i-1]*b%mod+(c[i]-'A'+1))%mod, ha1[i]=(ha1[i-1]*b1%mod1+(c[i]-'A'+1))%mod1, ha2[i]=(ha2[i-1]*b2%mod2+(c[i]-'A'+1))%mod2;//把每一位上的hash值求出来 ll ans=0; for(ll i=1;i<=n-m+1;i++)//枚举每一个题目要求长度的字串 { ll t=((ha[i+m-1]-ha[i-1]*p[m]%mod)+mod)%mod; ll tt=((ha1[i+m-1]-ha1[i-1]*p1[m]%mod1)+mod1)%mod1; ll ttt=((ha2[i+m-1]-ha2[i-1]*p2[m]%mod2)+mod2)%mod2;//把字串的不同hash值求出 if(!a[t]||!a1[tt]||!a2[ttt])//如果没有出现过就ans++ ans++; a[t]=1,a1[tt]=1,a2[ttt]=1; } printf("%lld",ans); return 0; }