判定数组中重复的数字时,可用hash记录;把数组变成字符串,判定字串是否重复时,可hash记录滑动窗口中的字串;但是字符串的hashcode获取可是要扫描字符串,而Character/Integer是直接获取原值作为hashcode(hashcode需要干扰之后作为hash值。),所以当字串字符进行二进制编码,让字符串映射到数值,快速获取hashcode.
public List<String> findRepeatedDnaSequences(String s) {
int n = s.length();
Set<String> pool = new HashSet<>();
Set<String> ans = new HashSet<>();
for(int i = 0;i < n - 9;i++){
String str = s.substring(i,i + 10);
if(pool.contains(str) && !ans.contains(str)) ans.add(str);
pool.add(str);
}
return new ArrayList<>(ans);
}
package everyday.bitManipulation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// 重复的DNA序列。
public class FindRepeatedDnaSequences {
/*
由于获取字符串的hashcode需要扫描整个字符串,所以可将少量字符串编码,即为了10子串的编码。
*/
public List<String> findRepeatedDnaSequences(String s) {
int n = s.length();
List<String> ans = new ArrayList<>();
if (n <= L) return ans;
// 初始化前9个数。
int x = 0, i = 0;
while (i < L - 1) x = x << 2 | fx.get(s.charAt(i++));
// 开始用滑动窗口拿子串的integer编码作为hash的key
Map<Integer, Integer> cnt = new HashMap<>();
while (i < n) {
x = (x << 2 | fx.get(s.charAt(i))) & (1 << L * 2) - 1;
cnt.put(x, cnt.getOrDefault(x, 0) + 1);
if (cnt.get(x) == 2) ans.add(s.substring(i - L + 1, i + 1));
++i;
}
return ans;
}
final static int L = 10;
static Map<Character, Integer> fx = new HashMap<>();
static {
fx.put('A', 0);
fx.put('C', 1);
fx.put('G', 2);
fx.put('T', 3);
}
}
1)二进制编码不仅可为字符串编码,常见的还有子集场景中,二进制映射到子集;二进制还可以将0/1 hash降维,比如一个值可以代替31位0-1hash,对付26个字母等绰绰有余。
2)二进制/位运算很重要,多练习,对很多问题优化有帮助。
3)字符串的hashcode获取不是O(1),而是O(n),字符/整数是O(1).