心得 字串雜湊二次學習

2021-10-20 13:30:17 字數 4506 閱讀 9858

如果手寫雜湊的話,主鍵key只能是int型別,如果主鍵是字串型別,值是int型別的對映時,就不能手寫雜湊了,就要使用unordered_map了。

將乙個字串轉化成乙個整數,並保證字串不同,其雜湊值不同。類似於整數的二進位制數表示的思想一樣。

因此將乙個字串看成乙個r進製數,對於乙個字串:s=s1s2…sn,如果是全都是小寫字母,可以將每個字元轉換成idx(si)=si-'a'+1,如果有大寫,有小寫,有數字不方便處理,可以直接用字元的ascii碼表示。

對字元的再次轉換編碼要注意不能從0開始,這就是為什麼+1的原因。

假設a的編碼為0,那麼"aa"、「aaa」、「aaaa」的雜湊值都是0,衝突嚴重。

將字串(r進製數)轉換成十進位制數通過雜湊函式來完成:hash[i] = ( hash[i-1]*r + idx(si) )%mod,i 從1 到 n,通過遞推中途可以求出字首子串"s1"、「s1s2」、「s1s2s3」……,一直到"s1s2……sn"的雜湊值,即最終的hash[n],我們的字串s的雜湊值,它的展開寫法為:hash[n]=(s1 × rn-1 + s2 × rn-2 + …… +sn-1 × r + sn )%mod

規定:為了統一化遞推公式,hash[0]=0

一般地,

r取131 或者13331 ;

mod取264 ,常利用unsigned long long自然溢位,相當於自動對264取模。因為ull也是64位

要注意,這種單雜湊方法的轉換並不能保證百分之百不衝突的,即可能存在兩個字串對應的雜湊值(十進位制數)一樣的情況,但是不安全因素很小,可以忽略,如果實在碰到了,考慮雙雜湊方法

在我們求乙個字串的雜湊值的時候,通常使用的是遞推的方法實現的,而不使用它的展開式,因此必然會求出這個字串的字首子串,可以用乙個陣列記錄下來,有了它的字首子串,我們就可以只用o(1)的複雜度求出這個字串任意子串的雜湊值:通過字首和的思想。

結論:求父串中從下標l下標r的字串對應的雜湊值:h[r]-h[l-1]×rr-l+1ull h[n];

字串:s=s1s2…sn,假設l=4,r=6,那麼就是要求出s4s5s6的雜湊值,即s4×r2 +s5×r+s6

我們已知h[6]=s1×r5 +s2×r4+s3×r3 + s4×r2 +s5×r+s6

h[4-1]=h[3]=s1×r2 +s2×r+s3

r3 ×h[3] =s1×r5 +s2×r4+s3×r3

但是觀察到h[6]和h[3]由於冪不匹配不能直接減,所以要先給h[3]乘乙個數,讓其和h[6]的前三位冪匹配,得到後三位,這個數就是r6-4+1 ,即r和l-1之間相差的位數,直接減即可。

rr-l+1 怎麼求呢?

在我們求hash[n]遞推求其字首的子串的時候,也可以求出rn ,這也是乙個遞推的過程:r^n = r^n-1 × r,用乙個陣列來維護,r[i]即為ri

最後,儲存字串的陣列,從下標0儲存還是從下標1儲存都行,但是為了和遞推陣列hash以及r保持一致,他們的遞推式裡都涉及了i-1,所以下標都從1開始比較好。

「hash[0]代表沒有子串,hash[1]代表只有1個字元的字首子串,hash[k]表示有k個字元的字首子串

給定乙個長度為n的字串,再給定m個詢問,每個詢問包含四個整數l1,r1,l2,r2,請你判斷[l1,r1]和[l2,r2]這兩個區間所包含的字串子串是否完全相同。

字串中只包含大小寫英文本母和數字。

輸入格式

第一行包含整數n和m,表示字串長度和詢問次數。

第二行包含乙個長度為n的字串,字串中只包含大小寫英文本母和數字。

接下來m行,每行包含四個整數l1,r1,l2,r2,表示一次詢問所涉及的兩個區間。

注意,字串的位置從1開始編號。這是題目要求的

輸出格式

對於每個詢問輸出乙個結果,如果兩個字串子串完全相同則輸出「yes」,否則輸出「no」。

每個結果佔一行。

資料範圍

1≤n,m≤105

輸入樣例:

8 3

aabbaabb

1 3 5 7

1 3 6 8

1 2 1 2

輸出樣例:

yes

noyes

#include

#define read(x,y) scanf("%d%d",&x,&y)

using

namespace std;

typedef

unsigned

long

long ull;

const

int n=

1e5+

10,r=

131;

char str[n]

;ull hs[n]

; 維護每個字首子串的雜湊值,

"hs[0]代表沒有子串,hs[1]代表只有1個字元的字首子串,hs[k]表示有k個字元的字首子串"

ull r[n]=;

//維護r^i,r^0=1,其它初始為0,也定義為ull型別,以防萬一

ull getsub

(int l,

int h)

// low high

intmain()

int l1,r1,l2,r2;

while

(num--

)return0;

}

給定乙個模式串s,以及乙個模板串p,所有字串中只包含大小寫英文本母以及阿拉伯數字。

模板串p在模式串s中多次作為子串出現。

求出模板串p在模式串s中所有出現的位置的起始下標。

輸入格式

第一行輸入整數n,表示字串p的長度。

第二行輸入字串p。

第三行輸入整數m,表示字串s的長度。

第四行輸入字串s。

輸出格式

共一行,輸出所有出現位置的起始下標(下標從0開始計數),整數之間用空格隔開。

資料範圍

1≤n≤105

1≤m≤106

輸入樣例:

3

aba5

ababa

輸出樣例:

0 2

#include

#define read(x) scanf("%d",&x)

using

namespace std;

typedef

unsigned

long

long ull;

const

int n=

1e5+

10,m=

1e6+

10,r=

131;

char ft[m]

,sn[n]

;//ft父串,sn子串

ull hs[m]

,h,r[m]=;

//hs陣列記錄ft父串的每個字首子串的雜湊值,h只記錄sn子串的雜湊值,r記錄r^i

"hs[0]代表沒有子串,hs[1]代表只有1個字元的字首子串,hs[k]表示有k個字元的字首子串"

ull getsub

(int l,

int h)

intmain()

//預處理子串

for(

int i=

1;i<=n;i++

) h=h*r+sn[i]

;for

(int i=

1;i<=m-n+

1;i++

)"提前在這裡i-1, hs[i+n-1]-hs[i-1] "

return0;

}

字串二次處理,補位 擷取

需求 字串a,處理成字串b 輸出結果要求 將字串處理成icount長度的字串,如果字串a的長度大於icount則依據字串b進行擷取 如果字串的長度小於icount的長度則進行補位。數字糾正,補全11位 如果位數大於11位則進行擷取 如果長度小於 11 basenum長度 則進行補位 原始字元 填補字...

字串雜湊

參照演算法筆記p109,甲級1039 先假設字串均由大寫字母a z構成。在這個基礎上,不妨把a z視為0 25,這樣就把26個大寫字母對應到了26進製中。接著,按照將26進製轉化為10進製的思路,由進製的轉換結論可知,在進製轉換過程中,得到的10進製肯定是唯一的,由此便可實現將字串對映為整數的需求 ...

字串雜湊

昨天做了一道字串雜湊的題,感覺還好理解。今天的題看了 不知道為什麼,搜來搜去發現不知道的東西還很多,網上找到的東西也都是很零散,書上也沒有系統的講解。先自己整理一下這些零散的知識 關於字串涉及到的演算法大概有 hash kmp trie ac自動機等等,現在還都不明白是怎麼回事,這次先研究字串has...