1044. 最长重复子串

1044. 最长重复子串

难度困难220

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

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

示例 1:

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

示例 2:

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

提示:

  • 2 <= s.length <= 3 * 104
  • s 由小写英文字母组成

分析:对于这种寻找最长/最短符合条件的字符序列问题,首先就是需要确定长度,那么实际上很容易会想到二分。我们来看一下二分如何使用:

        假设我们现在已知串str是一个满足要求的串,那么很显然str的子串也会满足要求。因此如我们发现在长度为len的情况下存在一个长度为len的str满足重复子串的要求,那么接下来的答案只可能>=len而不会<len,这符合二分的要求。

        接下来就是快速判断子串的存在性。假设我们现在已知子串str[i~j],那么下一个同长度的子串str[i+1~j+1]也很容易求出来,只要在str[i~j]的基础上去头加尾即可。

思路一:用哈希表存下子串-超时。

超时的原因在于,哈希表底层在对string求hash值的时候是需要遍历字符串的,因此实际上不能起到很大的优化效果。

class Solution {
public:

    // 还要记录起点位置

    //二分长度
    int check(string& s, int len){
        unordered_map<string, int> hash_map;
        string hash_str;
        int i;
        for(i = 0; i < len - 1; ++ i){
            hash_str.append(1, s[i]);
        }
        for(; i < s.size(); ++ i){
            hash_str.append(1, s[i]);
            if(hash_map.find(hash_str) != hash_map.end()){
                return hash_map[hash_str];
            }else{
                hash_map[hash_str] = i - len + 1;
                hash_str = hash_str.substr(1);
            }
        }
        return -1;
    }

    string longestDupSubstring(string s) {
        int l = 1, r = s.size(), m, temp_loc, len_ans = -1, loc = -1;
        while(l <= r){
            //cout << l << " " << r << endl;
            m = (l + r) >> 1;
            if((temp_loc = check(s, m)) != -1){
                l = m + 1;
                len_ans = m;
                loc = temp_loc;
            }else{
                r = m - 1;
            }
        }
        if(loc == -1) return "";
        return s.substr(loc, len_ans);
    }
};

思路2:将字符串映射为26进制数-部分数据溢出。

这种方法可以避免底层做哈希的时候还去遍历字符串,但有溢出的风险。而转成数字也可以快速地计算下一个哈希值,如12345,在子串长为4的时候,前一个是1234,那么下一个就是(1234 - 1 * 1000)* 10 + 5。

[注]:使用26进制数映射的方法会有溢出的风险,因为本题字符串长度还是比较大的。

class Solution {
public:

    // 还要记录起点位置

    //二分长度
    int check(string& s, int len){
        unordered_map<unsigned long long, int> hash_map;
        unsigned long long hash_value = 0, k = 1;
        int i;
        for(i = 0; i < len - 1; ++ i){
            k *= 26;
            hash_value = hash_value * 26 + s[i] - 'a';
        }
        for(; i < s.size(); ++ i){
            hash_value = hash_value * 26 + s[i] - 'a';
            if(hash_map.find(hash_value) != hash_map.end()){
                return hash_map[hash_value];
            }else{
                hash_map[hash_value] = i - len + 1;
                hash_value -= (s[i - len + 1] - 'a') * k; 
            }
        }
        return -1;
    }

    string longestDupSubstring(string s) {
        int l = 1, r = s.size(), m, temp_loc, len_ans = -1, loc = -1;
        while(l <= r){
            //cout << l << " " << r << endl;
            m = (l + r) >> 1;
            if((temp_loc = check(s, m)) != -1){
                l = m + 1;
                len_ans = m;
                loc = temp_loc;
            }else{
                r = m - 1;
            }
        }
        if(loc == -1) return "";
        return s.substr(loc, len_ans);
    }
};

思路3:据说可以用后缀数组???菜狗遁了遁了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值