LeetCode——最长重复子串?

Q:给出一个字符串 S,考虑其所有重复子串(S 的连续子串,出现两次或多次,可能会有重叠)。返回任何具有最长可能长度的重复子串。(如果 S 不含重复子串,那么答案为 ""。)
示例 1:
输入:"banana"
输出:"ana"
示例 2:
输入:"abcd"
输出:""

提示:
2 <= S.length <= 10^5
S 由小写英文字母组成。
通过次数1,383提交次数7,906

A:
1.字符串编码法:具体可以去看Rabin-Karp 字符串编码


代码:

public String longestDupSubstring(String S) {
        int len = S.length();
        int a = 26;
        if (len <= 1)
            return "";
        int[] nums = new int[len];
        for (int i = 0; i < len; i++) {
            nums[i] = S.charAt(i) - 'a';
        }
        for (int i = len - 1; i > 0; i--) {
            //i是子串长度
            HashMap<Long, Integer> map = new HashMap<>();
            long tmp = 0;
            long aL = 1;
            //算第一个hashcode
            for (int j = 0; j < i; j++) {
                tmp = (tmp * a + nums[j]) % MOD;
                aL = aL * a;
            }
            map.put(tmp, 0);
            for (int j = 1; j <= len - i; j++) {
                tmp = (tmp * a - nums[j - 1] * aL % MOD + nums[j + i - 1]) % MOD;
                if (map.containsKey(tmp)) {//这里做了一个判断,因为哈希表毕竟是有可能冲突的。但我没想好冲突发生怎么解决
                    String sub = S.substring(j, j + i);
                    String sub2 = S.substring(map.get(tmp), map.get(tmp) + i);
                    if (sub.equals(sub2))
                        return sub;
                }
                map.put(tmp, j);

            }
        }
        return "";
    }

但……超出时间限制了。

2.二分查找+字符串编码法

    public String longestDupSubstring(String S) {
		int len = S.length();
        int a = 26;  // 26进制
        long module = (long) Math.pow(2, 32);  // 取一个足够大的数,用以取模
        if(len <= 1)
            return "";
        int[] nums = new int[len];
        for(int i = 0; i < len; i++){
            nums[i] = (int) S.charAt(i) - (int) 'a';  // 只考虑小写字母
        }
        int low = 1;
        int high = len;
        while(low != high) {
        	int L = (high-low)/2 + low;
        	if(search(L, a, module, nums) != -1)
        		low = L + 1;
        	else
        		high = L;
        }
        int start = search(low-1, a, module, nums);
        if(start == -1)
        	return "";
        else
        	return S.substring(start, start+low-1);
    }
	// 返回重复字符串的起始位置
	// 参数:L-重复字符串的长度,a-进制,module-取模数,nums-字符串的编码数组
	public int search(int L, int a, long module, int[] nums) {
		int len = nums.length;
		HashSet<Long> hashSet = new HashSet<Long>(); 
        long tmp = 0;
        long aL = 1;
        for(int j = 0; j < L; j++){
            tmp = (tmp *a + nums[j]) % module;  // 防止溢出
            //System.out.println(tmp);
            aL = (aL*a) % module;
        }
        hashSet.add(tmp);
        for(int j = 1; j <= len - L; j++){  // 长度为i的窗口
            tmp = (tmp*a - nums[j-1]*aL%module + module) % module;  // 去掉前一位
            tmp = (tmp + nums[j+L-1]) % module;
            if(hashSet.contains(tmp))
                return j;
            hashSet.add(tmp);
        }
		return -1;
	}

3.还有后缀树法,等有时间做

原文地址:https://www.cnblogs.com/xym4869/p/12681329.html