[LeetCode] 1044. Longest Duplicate Substring
Posted CNoodle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LeetCode] 1044. Longest Duplicate Substring相关的知识,希望对你有一定的参考价值。
Given a string S
, consider all duplicated substrings: (contiguous) substrings of S that occur 2 or more times. (The occurrences may overlap.)
Return any duplicated substring that has the longest possible length. (If S
does not have a duplicated substring, the answer is ""
.)
Example 1:
Input: "banana" Output: "ana"
Example 2:
Input: "abcd" Output: ""
Note:
2 <= S.length <= 10^5
S
consists of lowercase English letters.
最长重复子串。题意是给一个input字符串,请你找出最长的重复子串。比如第一个例子,ana就出现了不止一次,且没有再长的子串出现多次的了。
思路是二分 + Rabin-Karp/rolling hash(滚动哈希)。二分的目的是在于加快找这个子串的速度。首先需要想通一个定理,如果能找到一个较长的重复子串,那么也就不需要再去找较短的重复子串了,因为较长的重复子串里面一定有较短的重复子串。比如ana里面一定包含一个更短的子串an和na。所以如果能在mid这个长度上找到这个重复子串,你就只需要往右边界靠拢去找是否有更长的重复子串而不需要去找更短的子串了。
rolling hash的目的是在于记录出现的子串并且节省空间。一般情况下的确可以用hashmap或者hashset存储,key是出现过的unique的子串;但是如果input字符串太长,也就意味着子串会很长,这样内存是会不够用的。rolling hash是将一个字符串转化为一个数字存在哈希表里面。rolling hash的实现方式是这样的,比如单词是banana。给每个字母赋值(0 - 25之间的数字),得到b, a, n, a, n, a -> [1, 0, 13, 0, 13, 0]。而计算rolling hash的方式是
比如b, a, n -> 1 * 100 + 0 * 10 + 13 * 1 = 100 + 0 + 13 = 113,然后113再 % 某一个数字,得到一个更小的数字(应该在Long型范围内)存在哈希表里面。
我这里只是给出一个例子,展示出rolling hash的概念,但是实际上如果真的只是按数位乘以10,一定会导致哈希冲突,所以做rolling hash的时候需要选择一个比较大的数字来替换10(代码里给的是Integer.MAX_VALUE,%的是256)。
时间 - ?
空间O(n) - hashmap
Java实现
1 class Solution { 2 private static final long q = (1 << 31) - 1; 3 private static final long R = 256; 4 5 public String longestDupSubstring(String S) { 6 int left = 2; 7 int right = S.length() - 1; 8 int start = 0; 9 int maxLen = 0; 10 11 while (left <= right) { 12 int len = left + (right - left) / 2; 13 boolean found = false; 14 15 Map<Long, List<Integer>> map = new HashMap<>(); 16 long hash = hash(S, len); 17 map.put(hash, new ArrayList<>()); 18 map.get(hash).add(0); 19 long RM = 1l; 20 for (int i = 1; i < len; i++) { 21 RM = (R * RM) % q; 22 } 23 24 loop: for (int i = 1; i + len <= S.length(); i++) { 25 hash = (hash + q - RM * S.charAt(i - 1) % q) % q; 26 hash = (hash * R + S.charAt(i + len - 1)) % q; 27 if (!map.containsKey(hash)) { 28 map.put(hash, new ArrayList<>()); 29 } else { 30 for (int j : map.get(hash)) { 31 if (compare(S, i, j, len)) { 32 found = true; 33 start = i; 34 maxLen = len; 35 break loop; 36 } 37 } 38 } 39 map.get(hash).add(i); 40 } 41 if (found) 42 left = len + 1; 43 else 44 right = len - 1; 45 } 46 return S.substring(start, start + maxLen); 47 } 48 49 private long hash(String S, int len) { 50 long h = 0; 51 for (int j = 0; j < len; j++) 52 h = (R * h + S.charAt(j)) % q; 53 return h; 54 } 55 56 private boolean compare(String S, int i, int j, int len) { 57 for (int count = 0; count < len; count++) { 58 if (S.charAt(i++) != S.charAt(j++)) 59 return false; 60 } 61 return true; 62 } 63 }
以上是关于[LeetCode] 1044. Longest Duplicate Substring的主要内容,如果未能解决你的问题,请参考以下文章