各位毒瘤大家好, 最近模擬賽考了一道trie+主席樹好題, 但大家都用hash水過了這道題(
包括我), 為了測試一下新搭建的HEAT OJ的hack功能,我將繼續扮演毒瘤的角色, 用毒瘤的藝術形象努力創造一個正能量的形象, 文體兩開花, 弘揚中華文化,右轉去BZOJ搞了一晚上hashkiller, 回來卡了單哈希(雙哈希是真滴卡不住
哈希(hash) :
利用大質數或其他對應函數把字符串轉為一個正整數來快速判斷字符串相等
通常可以模一個大質數或使用自然溢出
實現(例);
const int P = 1e9+7;
const int di = 1331;
hash[i] = (hash[i-1] * base + s[i]) % P;
其中\(base\), 我稱之為底數, P我稱之為模數, 事實上自然溢出相當於模了\(2^{64}\)
卡哈希的思想:
- 數學構造
- 隨機數據(依據生日悖論
Part 1 生日悖論:
如果一個班級有23個人, 那么其中有兩個人生日相同的概率超過50%
surprise 這與大部分人的直覺相違背, 所以稱之為生日悖論
為什么會這樣呢, 是自己的直覺不靠譜嗎?
不, 我們可以考慮另一個問題, 如果一個班里有23人包括自己, 有人生日和自己相同的概率是多少?
沒錯, 大概為\(6%\)左右, 這是與直覺近似的, 其實我們的直覺正是把"有人生日相同"和"有人生日和自己相同"的概念相混, 實際有人生日和自己相同的概率確實很小
證明可以用排列組合開心的手玩一下
性質:
樣本容量為\(n\), 有兩個樣本相同的概率為
Part 2 卡大質數hash (1000000009) :
考慮生日攻擊, 隨機一個1e5大小的字符串, 詢問長度為\(L\)的本質不同子串有多少個, 用大質數\(hash\)和后綴數組(也可以用自然溢出\(hash\))對拍, 輸出不同子串的終止位置, 拿\(fc\)命令對比一下, 找出\(hash\)值相等的不同子串
正確性如生日悖論, 大概有超過\(50%\)的幾率成功, 實際上質數不強的時候有很多相同
Part 3 卡自然溢出hash:
自然溢出\(hash\)在數據隨機的情況下正確性極高, 因為它的值域很大, 很難生日攻擊
考慮特殊構造:
對於底數為偶數:
構造\(aaaa\cdots aaaa\) 和 \(baaa\cdots aaaa\)兩個長度相等且長度大於64的串
底數的六十四次方以上模\(P\)就會為零, \(b\)和\(a\)也會被判為相等
對於底數為奇數:
不太好卡, 要用神仙的構造方法:
設一個串\(s[]\), \(s[1] = 'a'\) 設$ |s| = strlen(s + 1)$ 為\(s\)的長度
定義$ (!s)$ 為\(s\)中的字符全部\('a'變'b', 'b'變'a',\) 當然\(s\)中只含有\('a'\)和\('b'\)兩種字符
定義串\(S1 + S2\)為\(S1\)串在前\(S2\)串在后拼接起來, \(hash(s1)\) 為\(s1\)的哈希值
類似數列的, 我們定義一個"字符串列", 為一個字符串集合{\(S_n\)}, 后一個字符串可以通過前一個字符串推出
\(S_1 = "a"\)
則\(S_i\)的長度為\(2^{i-1}\)
希望得到 \(2^{64} | hash(S_i) - hash(!S_i)\) 設\(g_i = hash(S_i) - hash(!S_i)\)
\(g_i=g_{i-1}*(base^{2^{i-2}}-1)\) 每個 \((base^{2^{i-2}}-1)\) 都是偶數, 這使得g到第64項就就可以卡掉hash了,
但事實上12位以上就行, 因為
為一個偶數乘一個偶數, 而左邊的可以繼續遞歸下去, 所以原式整除\(2^i\) 然后就結束啦
長大后, 我要當毒瘤, 爺爺奶奶可高興了, 給我愛吃的...