ICode9

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

最长重复子串(Rabin-Karp算法)

2021-06-28 21:30:53  阅读:226  来源: 互联网

标签:子串 Karp 重复 int 哈希 字符串 长度 Rabin


目录

最长重复子串

C++代码


最长重复子串

1044. 最长重复子串

给出一个字符串 S,考虑其所有重复子串S 的连续子串,出现两次或多次,可能会有重叠)。

返回任何具有最长可能长度的重复子串。(如果 S 不含重复子串,那么答案为 ""。)

示例 1:

输入:"banana"
输出:"ana"

示例 2:

输入:"abcd"
输出:""

提示:

  1. 2 <= S.length <= 10^5
  2. S 由小写英文字母组成。

方法一:二分查找 + Rabin-Karp 字符串编码
分析

我们可以把这个问题分解成两个子问题:

从 1 到 N 中选取子串的长度 L;

检查字符串中是否存在长度为 L 的重复子串。

子任务一:二分查找

解决子问题一的最简单的方法是,我们从 L = N - 1 开始,依次递减选取子串的长度,并进行判断。如果发现存在长度为 L 的重复子串,就表示 L 是最长的可能长度。

但我们发现,如果字符串中存在长度为 L 的重复子串,那么一定存在长度为 L0 < L 的重复子串(选取长度为 L 的重复子串的某个长度为 L0 的子串即可),因此我们可以使用二分查找的方法,找到最大的 L。

子任务二:Rabin-Karp 字符串编码

我们可以使用 Rabin-Karp 算法将字符串进行编码,这样只要有两个编码相同,就说明存在重复子串。对于选取的长度 L:

使用长度为 L 的滑动窗口在长度为 N 的字符串上从左向右滑动;

检查当前处于滑动窗口中的子串的编码是否已经出现过(用一个集合存储已经出现过的编码);

若已经出现过,就说明找到了长度为 L 的重复子串;

若没有出现过,就把当前子串的编码加入到集合中。

C++代码

class Solution {
public:  
    //检查是针对存在重复子串 还是发生了哈希冲突
    bool check_hash(string& s, pair<int, int>& a, pair<int, int> b) {       //查看是否是真重复子串还是因为发生哈希碰撞而导致哈希值相同
        for (int i = a.first, j = b.first; i != a.second && j != b.second; ++i, ++j) {
            if (s[i] != s[j]) return false;
        }
        return true;
    }
    //检查字符串s中是否存在长度为len的重复子串,如果有则返回该子串,否则返回空字符串
    string check(string& s, int len){
        int base = 26;//二十六个字母对应二十六进制
        int mod = 1000007;//取模 避免哈希冲突

        int num = 0;
        for(int i = 0; i < len; i++)//计算出第一个len长度的哈希映射值
            num = (num * base + s[i] - 'a')%mod;
        
        unordered_map<int, pair<int, int>> seen;//存储的是哈希映射值及对应的坐标
        seen.insert({num, {0, len - 1}});

        int al = 1;//根据公式 计算出常数a的len次方
        for(int i = 1; i <= len; i++)
            al = (al * base)%mod;
        
        //遍历字符串 计算每一个长度为len的子串的哈希映射值
        for(int i = 1; i < s.size() - len + 1; i++){

            //计算长度为len的子串的哈希映射值
            num = num * base - ((s[i-1] - 'a') * al)%mod;
            while(num < 0) num += mod;
            num = (num + (s[i + len - 1] - 'a'))%mod;

            //查找是否有重复的子串
            if(seen.count(num))
                if(check_hash(s, seen[num], {i, i + len - 1}))
                    return s.substr(i, len);//如果是真的存在而不是因为哈希冲突,就返回这个子串
            seen.insert({num, {i, i + len - 1}});//如果是哈希冲突 就插入
        }
        return "";
    }

    //返回字符串s最长重复子串
    string longestDupSubstring(string s) {
        int m = s.size();
        int left = 0, right = m;
        string res = "";
        while(left < right){
            int mid = left + (right - left)/2;//二分法找到最长重复子串
            string tmp = check(s, mid);
            if(!tmp.empty()){//如果存在重复子串,就保存下来最长的一个重复子串
                res = tmp.size() > res.size() ? tmp : res;
                left = mid + 1;
            }else
                right = mid;

        }
        return res;
    }
};

标签:子串,Karp,重复,int,哈希,字符串,长度,Rabin
来源: https://blog.csdn.net/qq_41687938/article/details/118312273

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

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

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

ICode9版权所有