Rabin-Karp算法(加速字符串匹配)

Rabin-Karp算法

文章目录

  • Rabin-Karp算法
    • [1044. 最长重复子串](https://leetcode-cn.com/problems/longest-duplicate-substring/)

Rabin-Karp算法的作用就是实现字符串的快速对比,判断字符串是否相同。

算法的思想如下:

将字符串进行编码,利用HashMap实现快速的匹配对比判断。

所以关键是进行编码的规则,也就是利用hash函数进行编码映射。

eg abcd abc编码为 2 2 ∗ 0 + 2 1 ∗ 1 + 2 0 ∗ 2 = 4 2^2*0+2^1*1+2^0*2=4 220+211+202=4 但是由于字符串过长会导致溢出,通常后面后加上取模操作。对bcd进行编码 2 2 ∗ 1 + 2 1 + 2 0 ∗ 3 = 11 2^2*1+2^1+2^0*3=11 221+21+203=11

其实可以通过上一个abc的编码快速求出,也就是 ( 4 − 2 2 ∗ 0 ) ∗ 2 + 2 0 ∗ 3 = 11 (4-2^2*0)*2+2^0*3=11 (4220)2+203=11

但是当数量过多时,会造成hash冲突。为了避免hash冲突,使用两套进制和模的组合防止冲突。

1044. 最长重复子串

难度困难224

给你一个字符串 s ,考虑其所有 重复子串 :即,s 的连续子串,在 s 中出现 2 次或更多次。这些出现之间可能存在重叠。

返回 任意一个 可能具有最长长度的重复子串。如果 s 不含重复子串,那么答案为 ""

示例 1:

输入:s = "banana"
输出:"ana"

示例 2:

输入:s = "abcd"
输出:""

暴力超时

class Solution {
public:
    /* 
        首先分析题目
        求的是一个字符串s中的最长重复子串
        在s中出现多次,且可以重叠

        首先想想暴力法, 也就是对相同的下标进行逐个匹配

     */

    int Isok(int index_1,int index_2,string& s) {
        int ans = 0;
        int in1=index_1;
        int in2=index_2;
        while(index_2> index(26);
        // index.resize(6,{});
        for(int i=0;i1) {
                for(int j=0;j=s.size()-index[i][k]+1) {
                            continue;
                        }
                        int tmp = Isok(index[i][j],index[i][k],s);
                        if(tmp>ans.size()) {
                            ans=string(s.begin()+index[i][j],s.begin()+index[i][j]+tmp);
                        }
                    }
                }
            }
        }
        return ans;

    }
};

Rabin-Krap加二分

typedef pair pll;
class Solution {
public:
    long long pow(int a, int m, int mod) {
        long long ans = 1;
        long long contribute = a;
        while (m > 0) {
            if (m % 2 == 1) {
                ans = ans * contribute % mod;
                if (ans < 0) {
                    ans += mod;
                }
            }
            contribute = contribute * contribute % mod;
            if (contribute < 0) {
                contribute += mod;
            }
            m /= 2;
        }
        return ans;
    }

    int check(const vector & arr, int m, int a1, int a2, int mod1, int mod2) {
        int n = arr.size();
        long long aL1 = pow(a1, m, mod1);
        long long aL2 = pow(a2, m, mod2);
        long long h1 = 0, h2 = 0;
        for (int i = 0; i < m; ++i) {
            h1 = (h1 * a1 % mod1 + arr[i]) % mod1;
            h2 = (h2 * a2 % mod2 + arr[i]) % mod2;
            if (h1 < 0) {
                h1 += mod1;
            }
            if (h2 < 0) {
                h2 += mod2;
            }
        }
        // 存储一个编码组合是否出现过
        set seen;
        seen.emplace(h1, h2);
        for (int start = 1; start <= n - m; ++start) {
            h1 = (h1 * a1 % mod1 - arr[start - 1] * aL1 % mod1 + arr[start + m - 1]) % mod1;
            h2 = (h2 * a2 % mod2 - arr[start - 1] * aL2 % mod2 + arr[start + m - 1]) % mod2;
            if (h1 < 0) {
                h1 += mod1;
            }
            if (h2 < 0) {
                h2 += mod2;
            }

            // 如果重复,则返回重复串的起点
            if (seen.count(make_pair(h1, h2))) {
                return start;
            }
            seen.emplace(h1, h2);
        }
        // 没有重复,则返回-1
        return -1;
    }

    string longestDupSubstring(string s) {
        srand((unsigned)time(NULL));
        // 生成两个进制
        int a1 = random()%75 + 26;
        int a2 = random()%75 + 26;

        // 生成两个模
        int mod1 = random()%(INT_MAX - 1000000006) + 1000000006;
        int mod2 = random()%(INT_MAX - 1000000006) + 1000000006;
        int n = s.size();
        // 先对所有字符进行编码
        vector arr(n);
        for (int i = 0; i < n; ++i) {
            arr[i] = s[i] - 'a';
        }
        // 二分查找的范围是[1, n-1]
        int l = 1, r = n - 1;
        int length = 0, start = -1;
        while (l <= r) {
            int m = l + (r - l + 1) / 2;
            int idx = check(arr, m, a1, a2, mod1, mod2);
            if (idx != -1) {
                // 有重复子串,移动左边界
                l = m + 1;
                length = m;
                start = idx;
            } else {
                // 无重复子串,移动右边界
                r = m - 1;
            }
        }
        return start != -1 ? s.substr(start, length) : "";
    }
};

你可能感兴趣的:(算法,leetcode刷题之旅,算法,哈希算法,散列表)