leetcode1044. 最長重複子串 (Rabin-Karp + 二分 )

leetcode1044. 最長重複子串

題意

給出一個字符串 S,考慮其所有重複子串(S 的連續子串,出現兩次或多次,可能會有重疊)。

返回任何具有最長可能長度的重複子串。(如果 S 不含重複子串,那麼答案爲 “”。)

思路

這裏兩個相同子串的最大長度滿足遞增性, 所以可以用二分的枚舉這個長度值m。

那麼問題就變成在一個字符串裏枚舉是否有兩個長度爲m的相同子串。

枚舉過程,我們可以想象是就是一個大小爲m的窗口滑動的過程。 總的時間複雜度是 滑動過程乘以比較窗口內字符串是否出現 O(len(S)*len(S))。

在這裏新學習Rabin-Karp算法可以實現,O(1)計算出窗口內字符串的hash值,從而判斷是否出現過。

具體的Rabin-Karp算法就是將一個字符串用公式計算成唯一的hash數值,原理很簡單。

計算公式如下:
在這裏插入圖片描述
其中a是每個字符串單個字符的種類,這樣就能保證每個字符串hash值是唯一的,比如全是小寫字母的字符串,a可以是26。

當窗口滑動時,新的字符串hash計算公式也很簡單,如下:
在這裏插入圖片描述

代碼

class Solution:
    def rabin_karp_check(self, nums, a ,m, n) :
        p = pow(a , m-1, self.mod)
        import functools
        cur = functools.reduce(lambda x,y: (x*a+y) % self.mod, nums[:m])
        seed = {cur}
        for index in range(m, n):
            cur = ((cur - p * nums[index-m])* a + nums[index])%self.mod
            if cur in seed :
                return index - m +1
            seed.add(cur)
        return -1

    def longestDupSubstring(self, S: str) -> str:
        self.mod = 2**63 -1
        l , r = 1, len(S)
        nums = [ord(c) - ord('a') for c in S]  #把字符映射爲數值
        pos = 0
        while l <=r :
            mid = int((l+r) /2)
            index = self.rabin_karp_check(nums, 26, mid, len(S))
            if index != -1:
                l= mid + 1
                pos = index
            else : r = mid - 1
        return S[pos: pos +l-1]

這題補充了functools.reduce()的用法。

另外注意這題數據量很大,我用c++寫的版本,mod 取到最大還是hash後還是會衝突,過不了。

Wrong的代碼先貼在這,有時間再糾結怎麼改對…

class Solution {
public:
    int rabin_karp_check(vector<int>nums, long long  a, int m, int n){
        set<int> cot;
        long long cur = 0, mod = 6*(1<<20)+1;
        long long p =1;
        for(int i=0;i<m;i++){
            cur = (nums[i] + cur*a%mod) % mod;
            p = p*a % mod;
        }
        cot.insert(cur);
        for(int i = m;i<n;i++){
            cur = (cur * a % mod- nums[i-m]*p %mod + nums[i]) % mod;
            //cout<<cur<<endl;
            if(cot.count(cur)>0){
                return i-m+1;
            }
            else{
                cot.insert(cur);
            }
        }
        return -1;
    }

    string longestDupSubstring(string S) {
         int l =1 ,r= S.length();
         vector<int> nums;
         for(char c:S){
            nums.push_back(c - 'a');
         }
         int res =0;
         while(l<=r){
            int mid = (l+r) *0.5;
            int index = rabin_karp_check(nums, 26, mid, S.length());
            if(index != -1){
                res = index;
                l = mid +1;
            }
            else{
                r= mid - 1;
            }
         }
         return S.substr(res, res+l-1);
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章