hash 值重复_“重复”相关的问题
重復(fù)子串比較的核心是使用 Rabin-Karp (Rolling Hash)。
Rabin-Karp字符串編碼的本質(zhì)是對(duì)字符串進(jìn)行哈希,將字符串之間的比較轉(zhuǎn)化為編碼之間的比較有N個(gè)不同的字符,可以將字符組成的串,映射成 N進(jìn)制表示的10進(jìn)制數(shù)。每一個(gè)數(shù)可以代表一種字符。 去頭加尾的哈希計(jì)算可以在
的時(shí)間內(nèi)完成題目A(困難)
1044. 最長(zhǎng)重復(fù)子串給出一個(gè)字符串 S,考慮其所有重復(fù)子串(S 的連續(xù)子串,出現(xiàn)兩次或多次,可能會(huì)有重疊)。
返回任何具有最長(zhǎng)可能長(zhǎng)度的重復(fù)子串。(如果 S 不含重復(fù)子串,那么答案為 ""。)示例 1:輸入:"banana" 輸出:"ana" 示例 2:輸入:"abcd" 輸出:""
二分查找 + Rabin-Karp 字符串編碼
子任務(wù)一:二分查找
如果字符串中存在長(zhǎng)度為 L 的重復(fù)子串,那么一定存在長(zhǎng)度為 K < L 的重復(fù)子串(選取長(zhǎng)度為 L 的重復(fù)子串的某個(gè)長(zhǎng)度為 K 的子串即可),因此我們可以使用二分查找的方法,找到最大的 L。
子任務(wù)二:是否存在長(zhǎng)度為L(zhǎng)的重復(fù)子串(Rabin-Karp 字符串編)
使用 Rabin-Karp 算法將字符串進(jìn)行編碼,這樣只要有兩個(gè)編碼相同,就說(shuō)明存在重復(fù)子串。
對(duì)于選取的長(zhǎng)度 L:使用長(zhǎng)度為 L 的滑動(dòng)窗口在長(zhǎng)度為 N 的字符串上從左向右滑動(dòng);
哈希計(jì)算公式字符一共有26,a最小可以取26.
檢查當(dāng)前處于滑動(dòng)窗口中的子串的編碼是否已經(jīng)出現(xiàn)過(guò)(用一個(gè)集合存儲(chǔ)已經(jīng)出現(xiàn)過(guò)的編碼)。若已經(jīng)出現(xiàn)過(guò),就說(shuō)明找到了長(zhǎng)度為 L 的重復(fù)子串;若沒(méi)有出現(xiàn)過(guò),就把當(dāng)前子串的編碼加入到集合中。
注意事項(xiàng):取模
最后一個(gè)需要解決的問(wèn)題是,在實(shí)際的編碼計(jì)算中,hash值、
可能會(huì)非常大,在 C++ 和 Java 語(yǔ)言中,會(huì)導(dǎo)致整數(shù)的上溢出。所以,需要對(duì)編碼值進(jìn)行取模,將編碼控制在一定的范圍內(nèi),防止溢出,即h = h % modulus。h = (h * a - nums[start - 1] * aL % modulus + modulus) % modulus; h = (h + nums[start + L - 1]) % modulus;注意事項(xiàng)2:哈希沖突
取模會(huì)導(dǎo)致哈希沖突,既是有可能兩個(gè)字符串是不同的,但是哈希值相同。后續(xù)的話學(xué)習(xí)一下hash沖突的解決方式。
最終代碼
public int search(int L, int n, int[] nums) {// roll hash的base值,26個(gè)字符,用26進(jìn)制玩。int a = 26;// 防止hash值溢出,使用的mod數(shù)long modulus = (long) Math.pow(10, 16);// 計(jì)算長(zhǎng)度為L(zhǎng)的第一個(gè)子串的 roll hash 值long h = 0;for (int i = 0; i < L; ++i)h = (h * a + nums[i]) % modulus;// 存儲(chǔ)已出現(xiàn)過(guò)的hash值HashSet<Long> seen = new HashSet<>();seen.add(h);// 第一位的26的L冪 取模 : aL % moduluslong aL = 1;for (int i = 1; i <= L; ++i)aL = (aL * a) % modulus;for (int start = 1; start < n - L + 1; ++start) {// compute rolling hash in O(1) timeh = (h * a - nums[start - 1] * aL % modulus + modulus) % modulus;h = (h + nums[start + L - 1]) % modulus;if (seen.contains(h)) return start;seen.add(h);}return -1;}public String longestDupSubstring(String S) {int n = S.length();// 將字符串映射成 hash值 (26進(jìn)制的數(shù)值)// 實(shí)現(xiàn)常數(shù)時(shí)間復(fù)雜度的滑動(dòng)窗口int[] nums = new int[n];for (int i = 0; i < n; ++i)nums[i] = (int) S.charAt(i) - (int) 'a';// 二分搜索, L = 要定位的重復(fù)字符串的長(zhǎng)度int minRepeat = 1, maxRepeat = n;int L;while (minRepeat != maxRepeat) {L = minRepeat + (maxRepeat - minRepeat) / 2;if (search(L, n, nums) != -1)minRepeat = L + 1;elsemaxRepeat = L;}int start = search(minRepeat - 1, n, nums);return start != -1 ? S.substring(start, start + minRepeat - 1) : "";}總結(jié)
以上是生活随笔為你收集整理的hash 值重复_“重复”相关的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 方舟原始恐惧代码_源代码分支管理模式丨中
- 下一篇: python最适合做什么生意赚钱投资小_