日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

哈希学习笔记

發(fā)布時間:2023/12/3 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 哈希学习笔记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

hash學(xué)習(xí)筆記

  • 常用函數(shù): $ hash[i] = \sum _{j=i} ^{len-1} {s[j]*X^{j-i}}, X ≥ |字符集| $ 取多個模,對于一個子串\(s[i]s[i+1]..s[j]\)\(hash = hash[i] - hash[j+1]*X^{j-i+1}\),預(yù)處理\(hash[i]\)以及\(X^i\)即可\(O(1)\)求出所有的子串hash值
  • 一般運(yùn)用hash進(jìn)行快速查找,狀態(tài)壓縮,其他hash相關(guān):康托展開及逆展開,ELFhash

  • Problem A

  • 題意:給定一字符串S,詢問\(LCP(x,y)\),即從x,y分別開始的最長公共前綴
  • 做法:二分最長公共前綴的長度L,\(O(1)\)判斷兩個串的\(hash\)是否相同
  • #include <bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;++i) #define frep(i,a,b) for(int i=a;i>=b;--i) typedef unsigned long long ll; const int N = 100007; const ll base = 107; using namespace std; int n,q; ll P[11][N], h[11][N], mod[11]; int cc; char s[N]; void init_hash() {cc=0;mod[++cc] = 1e9+7;mod[++cc] = 1000007;mod[++cc] = 998244353;mod[++cc] = 100007;P[0][0]=1; rep(i,1,100) P[0][i]=P[0][i-1]*base;frep(i,n,1) h[0][i] = h[0][i+1]*base + s[i];rep(i,1,cc){P[i][0]=1;rep(j,1,100) P[i][j]=(P[i][j-1]*base)%mod[i];}rep(i,1,cc)frep(j,n,1)h[i][j] = ((h[i][j+1]*base)%mod[i]+s[j])%mod[i]; } ll ask0(int x,int y) {if(x>y)swap(x,y);return h[0][x]-h[0][y+1]*P[0][y-x+1]; } ll ask1(int x,int y,int z) {if(x>y)swap(x,y);return (h[z][x]%mod[z]-(h[z][y+1]*P[z][y-x+1])%mod[z]+mod[z])%mod[z]; } int ck(int x,int y,int L) {if(ask0(x,x+L-1)!=ask0(y,y+L-1))return 0;rep(i,1,cc){if(ask1(x,x+L-1,i)!=ask1(y,y+L-1,i)) return 0;}return 1; } int solve(int x,int y) {int l=1,r=min(n-x+1,n-y+1),mid,ans=0;if(s[x]!=s[y])return 0;while(l<=r) {mid = (l+r)>>1;if(ck(x,y,mid))l=mid+1,ans=mid;else r=mid-1;}return ans; }int main() {scanf(" %s",s+1);n=strlen(s+1);init_hash();scanf("%d",&q);while(q--) {int x,y;scanf("%d %d",&x,&y);printf("%d\n",solve(x,y));}return 0; }

    Problem B

  • 題意:給定一字符串S,1)詢問\(LCP(x,y)\),即從x,y分別開始的最長公共前綴;2)修改位置x的字符為y
  • 做法1:線段樹維護(hù)維護(hù)每個位置的hash[i],修改操作相當(dāng)于給區(qū)間[1,x]加上
    \[(y-s[x])*X^x,(y-s[x])*X^x-1,...(y-s[x])*X^0\]
    這一等比數(shù)列,查詢就是單點(diǎn)查詢,再用上一題的做法即可。區(qū)間加公比固定的等比數(shù)列
  • 做法2:線段樹維護(hù)\(S[i]*X^i\)修改就是單點(diǎn)修改,查詢\([i,j]\)區(qū)間和之后要除以\(X^i\),要預(yù)處理對應(yīng)模數(shù)的逆元。代碼回頭補(bǔ)。

  • Problem C [BZOJ1014]

  • 題意:給定一字符串S,1)詢問\(LCP(x,y)\),即從x,y分別開始的最長公共前綴;2)修改位置x的字符為y;3)在S的第x個字符后添加一個字符
  • 做法:splay每個節(jié)點(diǎn)維護(hù)size[i]:子樹大小,hash[i]:以i為根節(jié)點(diǎn)的子樹的hash值,\(hash[fa]=hash[lson]+s[fa]*X^{size[lson]}+hash[rson]*X^{size[lson]+1}\),對于查詢,每次把一個后綴旋轉(zhuǎn)到一顆子樹上,子樹的根節(jié)點(diǎn)的hash值就是答案,對于修改,直接修改從這個點(diǎn)到根的路徑上的hash值,對于插入,直接splay插入更新size和hash。代碼等到學(xué)splay專題的時候我再努力寫吧。

  • Problem D [BZOJ3555]

  • 題意:n個長度為L的互不相同的字符串,求有多少對字符串互相相似。相似:如果兩個字符串恰好有一個字符不同,則他們相似。n≤30000,L≤200,|字符集|≤64
  • 做法:預(yù)處理每個串前綴的hash值,枚舉哪一位不同,排序計(jì)算貢獻(xiàn)。關(guān)鍵是如何快速求出去除第i位的字符串的hash值。下面進(jìn)行推導(dǎo):
    對于字符串S, \(hash[i] = S[1]*X^{i-1} + S[2]*X^{i-2} + ... + S[i]\)
    除去S串的第\(x\)個字符的字符串\(Hash = (S[1]*X^{n-1} + S[2]*X^{n-2} + ... + S[x-1]*X^{n-x+1})+(S[x+1]*X^{n-x}+S[x+2]*X^{n-x-1}...+S[n])\\= hash[x-1]*X^{n-x+1} +(hash[n]-hash[x]*X^{n-x})\)
  • #include <bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;++i) #define frep(i,a,b) for(int i=a;i>=b;--i) typedef unsigned long long ll; const int N = 50100; const ll base = 107; using namespace std; int n,q,L,X; ll P[211], h[N][211],a[N]; char s[211]; ll cal(int s,int x) {return h[s][x-1]*P[L-x+1] +(h[s][L]-h[s][x]*P[L-x]); } int main() {scanf("%d%d%d",&n,&L,&X);P[0]=1;rep(i,1,L)P[i]=P[i-1]*base;rep(i,1,n) {scanf(" %s",s+1);rep(j,1,L) h[i][j]=h[i][j-1]*base+s[j];}int ans=0;rep(i,1,L){rep(j,1,n)a[j]=cal(j,i);sort(a+1,a+n+1);int tmp = 0;rep(j,2,n){if(a[j]!=a[j-1])tmp=0;else ++tmp;ans += tmp;}}printf("%d\n",ans);return 0; }

    二維hash[BZOJ2462]

  • 題意:給定一個M行N列的01矩陣,以及Q個A行B列的01矩陣,你需要求出這Q個矩陣哪些在
    原矩陣中出現(xiàn)過。所謂01矩陣,就是矩陣中所有元素不是0就是1。
  • 做法:類似一維hash,先豎著hash,再橫著hash,兩次要用不同的base,查詢子矩形方法類似于差分。
  • hints:本題要對矩陣的hash值二次取mod,用map會tle
  • #include <bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;++i) typedef unsigned long long ll; const int N = 1007; const ll base1 = 107; const ll base2 = 31; const ll mod = 10000007; using namespace std; int n,m,a,b,q; ll P1[N], P2[N], h[N][N]; bool vis[mod+1]; char mp[N][N]; void init_hash() {P1[0]=1; rep(i,1,100) P1[i]=P1[i-1]*base1;P2[0]=1; rep(i,1,100) P2[i]=P2[i-1]*base2;rep(i,1,n)rep(j,1,m) h[i][j] += h[i-1][j]*base1 + mp[i][j];rep(i,1,n)rep(j,1,m) h[i][j] += h[i][j-1]*base2; } ll ask(int x,int y,int xl,int yl) {return h[x][y]-h[x-xl][y]*P1[xl]-h[x][y-yl]*P2[yl]+h[x-xl][y-yl]*P1[xl]*P2[yl]; } int main() {scanf("%d%d%d%d",&n,&m,&a,&b);rep(i,1,n)scanf(" %s",mp[i]+1);init_hash();rep(i,a,n)rep(j,b,m) vis[ask(i,j,a,b)%mod]=1;scanf("%d",&q);while(q--) {rep(i,1,a)scanf(" %s",mp[i]+1);memset(h,0,sizeof(h));rep(i,1,a)rep(j,1,b)h[i][j]+=h[i-1][j]*base1+mp[i][j];rep(i,1,a)rep(j,1,b)h[i][j]+=h[i][j-1]*base2;printf("%d\n",vis[h[a][b]%mod]);}return 0; }

    轉(zhuǎn)載于:https://www.cnblogs.com/RRRR-wys/p/9256622.html

    總結(jié)

    以上是生活随笔為你收集整理的哈希学习笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。