如果手寫雜湊的話,主鍵key只能是int型別,如果主鍵是字串型別,值是int型別的對映時,就不能手寫雜湊了,就要使用unordered_map了。
將乙個字串轉化成乙個整數,並保證字串不同,其雜湊值不同。類似於整數的二進位制數表示的思想一樣。
因此將乙個字串看成乙個r進製數,對於乙個字串:s=s1s2…sn,如果是全都是小寫字母,可以將每個字元轉換成idx(si)=si-'a'+1
,如果有大寫,有小寫,有數字不方便處理,可以直接用字元的ascii碼表示。
對字元的再次轉換編碼要注意不能從0開始,這就是為什麼+1的原因。將字串(r進製數)轉換成十進位制數通過雜湊函式來完成:假設a的編碼為0,那麼"aa"、「aaa」、「aaaa」的雜湊值都是0,衝突嚴重。
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]和h[3]由於冪不匹配不能直接減,所以要先給h[3]乘乙個數,讓其和h[6]的前三位冪匹配,得到後三位,這個數就是r6-4+1 ,即r和l-1之間相差的位數,直接減即可。我們已知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
rr-l+1 怎麼求呢?最後,儲存字串的陣列,從下標0儲存還是從下標1儲存都行,但是為了和遞推陣列hash以及r保持一致,他們的遞推式裡都涉及了i-1,所以下標都從1開始比較好。在我們求hash[n]遞推求其字首的子串的時候,也可以求出rn ,這也是乙個遞推的過程:
r^n = r^n-1 × r
,用乙個陣列來維護,r[i]即為ri
「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
yesnoyes
#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...