ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-12-23 15:33:28  阅读:273  来源: 互联网

标签:子串 index Karp int contribute long 算法 ans Rabin


Rabin-Karp算法

文章目录


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 22∗0+21∗1+20∗2=4 但是由于字符串过长会导致溢出,通常后面后加上取模操作。对bcd进行编码 2 2 ∗ 1 + 2 1 + 2 0 ∗ 3 = 11 2^2*1+2^1+2^0*3=11 22∗1+21+20∗3=11

其实可以通过上一个abc的编码快速求出,也就是 ( 4 − 2 2 ∗ 0 ) ∗ 2 + 2 0 ∗ 3 = 11 (4-2^2*0)*2+2^0*3=11 (4−22∗0)∗2+20∗3=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<s.size()) {
            if(s[index_1]==s[index_2]) {
                ans++;
                index_1++;
                index_2++;
            } else {
                break;
            }
        }
        // if(in1+ans<in2) {
        //     ans=0;
        // }
        return ans;
    }

    string longestDupSubstring(string s) {
        vector<vector<int>> index(26);
        // index.resize(6,{});
        for(int i=0;i<s.size();i++) {
            index[s[i]-'a'].push_back(i);
        }

        string ans;
        // 遍历
        for(int i=0;i<index.size();i++) {
            if(index[i].size()>1) {
                for(int j=0;j<index[i].size();j++) {
                    for(int k=j+1;k<index[i].size();k++) {
                        if(ans.size()>=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<long long, long long> 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<int> & 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<pll> 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<int> 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) : "";
    }
};

标签:子串,index,Karp,int,contribute,long,算法,ans,Rabin
来源: https://blog.csdn.net/ahelloyou/article/details/122108680

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有