题目大意
给定一个DNA序列,每个字符为26个小写字母之一。
有 $m$ 个询问,每次指定两个子串,问是否一样。
思路
显然的字符串hash+前缀和。
关于字符串前缀和:
其实可以理解成base进制数,每次多一位就 *base
,相当于左移一位,然后加上个位;
然后比较两个相等,就把左端点之前一个乘到右端点那一位,然后相减,不理解可以拿十进制模拟一下。
代码
#include <bits/stdc++.h>
#define ull unsigned long long
using namespace std;
//const ll P=1e9+7;
const ull base=131; //挺好用的一个hash base
const int N=1e6+10;
int m;
ull sum[N],powe[N];
char s[N];
int main()
{
scanf( "%s",s+1 );
powe[0]=1; int len=strlen(s+1);
for ( int i=1; i<=len; i++ )
{
//sum[i]=(sum[i-1]*base+(s[i]-'a'+1))%P;
sum[i]=(sum[i-1]*base+(s[i]-'a'+1)); //字符串hash前缀和
powe[i]=powe[i-1]*base; //其实可以把字符串hash理解为base进制数
//powe[i]=powe[i-1]*base%P;
}
scanf( "%d",&m );
while ( m-- )
{
int l1,l2,r1,r2; scanf( "%d%d%d%d",&l1,&r1,&l2,&r2 );
//ll res1=sum[r1]-sum[l1-1]*powe[r1-l1+1]%P;
ull res1=sum[r1]-sum[l1-1]*powe[r1-l1+1];
ull res2=sum[r2]-sum[l2-1]*powe[r2-l2+1];
//ll res2=sum[r2]-sum[l2-1]*powe[r2-l2+1]%P;
if ( res1==res2 ) printf( "Yes\n" );
else printf( "No\n" );
}
}
后记
一开始是想用 $1e9+7$ 作为模数的,因为听说 $ull$ 很容易被搞掉,而且我只写了单哈希。
不过后来不知道为啥取模取出锅了……调不出来还是用了。(详见代码注释掉的几行)
如果有人会取模做法的话求教qwq
upd
原来是负数取模锅掉了……
减法那里取模改成(sum[i-1]*base+(s[i]-‘a’+1)+P)%P;C++负数取模锅不是众所周知了吗qwq
哦!我脑子抽了QAQ感谢
不是这个原因,这里的
s[i]-'a'+1
一定是>=1
的,不是这个原因我和答主遇到一样的问题,也是
1e9+7
出现问题,后来找到错误原因了:就是乘法的地方会溢出int
,在乘法的地方(一共有四处需要修改)强制临时转一下longlong
再取模p
回到int
就行了。这是我的代码,虽然是java,但是也可以看思路。程序跳转