[HDU5685]2016"百度之星" - 資格賽 Problem A
阿新 • • 發佈:2017-08-13
++i 字符 pre fine 字符串 scan div true 擴展歐幾裏得
題目大意:給你一個字符串,和一些問題,每個問題問你[l,r]子串的哈希值是多少。
哈希值計算方法為:$H(s)=\prod _{i=1} ^{i\leq len(s)}(s_i-28)(mod\ 9973)$。
其中$s_i$代表 S[i] 字符的 ASCII 碼。
解題思路:我們知道,要算區間[l,r]所有的和,就可以用$O(n)$的時間預處理出數組t,令$t[i]$表示前i個數的和,那麽$t[r]-t[l-1]$即為區間[l,r]所有之和,詢問時間復雜度$O(1)$,這就是維護前綴和的做法。
那麽這道題,我們也可以維護一個前綴,令$t[i]$表示前i個字符的哈希值,然後[l,r]子串的哈希值就為$\frac{t[r]}{t[l-1]}$。
等等,有個取模運算,那這樣做不就WA了?沒事,我們把$t[r]$除以$t[l-1]$改成$t[r]$乘($t[l-1]$的乘法逆元)即可。計算乘法逆元用擴展歐幾裏得算法即可。
時間復雜度$O(Tn\log (a+b))$,其中T為數據組數。
註意事項:$t[0]$一定要賦成1,否則你怎麽算答案都是0!!!
C++ Code:
#include<cstdio> #include<cstring> using namespace std; #define p 9973 int n; char s[100004]; int t[100004]; int exGcd(int a,int b,int& x,int& y){ if(b==0){ x=1,y=0; return a; } int gcd=exGcd(b,a%b,x,y); int q=x; x=y; y=q-a/b*y; return gcd; } int calc(int l,int r){ int x,y; exGcd(t[l-1],p,x,y); return t[r]*((x%p+p)%p)%p; } int main(){ while(scanf("%d",&n)!=EOF){ scanf("%s",s+1); t[0]=1; int len=strlen(s+1); for(int i=1;i<=len;++i)t[i]=t[i-1]*(s[i]-28)%p; int l,r; while(n--){ scanf("%d%d",&l,&r); printf("%d\n",calc(l,r)); } } return 0; }
[HDU5685]2016"百度之星" - 資格賽 Problem A