1.二进制中1的位数
题目:
数组中所有数对的汉明距离
题目:477. Total Hamming Distance
解:统计第0位上的汉明距离,第1位上的汉明距离,… 第32位上的汉明距离, 它们的和就是总的汉明距离。
第i位的汉明距离,等于该位上1和0组合的不同个数,等于1的个数乘以0的个数。
Integer.bitCount
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
Long.bitCount
public static int bitCount(long i) {
// HD, Figure 5-14
i = i - ((i >>> 1) & 0x5555555555555555L);
i = (i & 0x3333333333333333L) + ((i >>> 2) & 0x3333333333333333L);
i = (i + (i >>> 4)) & 0x0f0f0f0f0f0f0f0fL;
i = i + (i >>> 8);
i = i + (i >>> 16);
i = i + (i >>> 32);
return (int)i & 0x7f;
}
1.对于较长的二进制数据使用BitSet
题目:1178. Number of Valid Words for Each Puzzle
给定两个字符串数组A和B,如果对B中的一个字符串p,A的所有字符串w都满足如下条件:
I. w的所有字符都出现在p中
II. p的第一个字符出现在w中
对B中的每一个字符串B[i],求A中满足上述条件的数目
使用java.lang.BitSet
class Solution {
boolean[][] wordChars;
boolean[][] puzzleChars;
public List<Integer> findNumOfValidWords(String[] words, String[] puzzles) {
int n = words.length;
int m= puzzles.length;
List<Integer> ans = new ArrayList<>(m);
// wordSet[i] is a set of puzzles that contains i
BitSet[] wordSet = new BitSet[26];
for(int i=0;i<m;++i){
for(int j=0,jn=puzzles[i].length();j<jn;++j){
int c = puzzles[i].charAt(j) - 'a';
if(wordSet[c]==null){
wordSet[c] = new BitSet(n);
}
wordSet[c].set(i);
}
}
// check each word if they are in puzzles
// check each word if one character is the start of a puzzle
for(int i=0;i<m;++i){
ans.add(0);
}
boolean[] visited = new boolean[26];
BitSet set = new BitSet(m);
for(int i=0;i<n;++i){
boolean empty = false;
// reset all to 1
set.set(0,m);
for(int j=0,jn=words[i].length();j<jn;++j){
int c = words[i].charAt(j) - 'a';
if(visited[c]){
continue;
}
visited[c]=true;
if(wordSet[c]==null){
empty = true;
break;
}
set.and(wordSet[c]);
if(set.isEmpty()){
break;
}
}
if(!empty){
set.stream().forEach(k->{
if(visited[puzzles[k].charAt(0)-'a']){
ans.set(k, ans.get(k)+1);
}
});
}
for(int j=0;j<26;++j){
visited[j] =false;
}
}
return ans;
}
}
2.二分背包
题目: 1125. Smallest Sufficient Team
解:如果 t 可以通过 t1|p1, t2|p2, t3|p3运算而来,则可知t的值就是三者的最小值: f(t) = min { f(t1|p1), f(t2|p2), f(t3|p3) } + 1.
我们只需要确保在求t的值时,所有t1,t2,t3的值已经确定。为了做到这一点,我们确保求t的过程中是递增的,就是 t=t1|p1 > t1.
为什么要确保这个顺序呢?为了避免后续重新更新t1,造成t也需要更新。(退回到Bellman-Ford算法的SPFA实现).
为了证明这个顺序,我们使用归纳法证明,如果求解的集合是 {t1,t2,t3, …}, 取集合中任意ti结合pi产生t=ti|pi, 且t不属于该集合,ti属于该集合。则显然t的解是确定的 。
class Solution {
public int[] smallestSufficientTeam(String[] req_skills, List<List<String>> people) {
int n = req_skills.length;
int size = 1<<n;
int m = people.size();
Map<String,Integer> mp = new HashMap<>(n);
for(int i=0;i<n;++i){
mp.put(req_skills[i], i);
}
int[] pbit = new int[m];
for(int i=0;i<m;++i){
for(String s:people.get(i)){
pbit[i] |= 1<<mp.get(s);
}
}
long[] dp = new long[size];
Arrays.fill(dp,-1L);
dp[0] = 0;
for(int i=0;i<size;++i){
for(int j=0;j<m;++j){
int nextState = i|pbit[j];
if(nextState != i && (dp[nextState]==-1L ||(Long.bitCount(dp[i])+1 < Long.bitCount(dp[nextState]) ))){
dp[nextState] = dp[i] | (1L<<j);
}
}
}
long s = dp[size-1];
int[] res = new int[Long.bitCount(s)];
int idx=0;
for(int i=0;i<m;++i){
if( (s&1)==1){
res[idx++]=i;
}
s>>>=1;
}
return res;
}
}
3.x+y=x|y
题目:给定x,求第k个满足x+y=x|y的数
解:x+y=x|y的充分必要条件就是x和y之间不存在任何同时为1的位。
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
long x = scanner.nextLong();
long k = scanner.nextLong();
long t = ~x;
long mask = 1;
for(int i=0;i<64;++i, mask<<=1){
if((mask & t)==0)continue;
if( (k&1)==0){
// clear ith bit
t &= (~mask);
}
k>>>=1;
}
System.out.println(t);
}
}
4. x xor y > m (使用Trie,TODO完善说明)
题目
给定整数m以及n各数字A1,A2,…An,将数列A中所有元素两两异或,共能得到n(n-1)/2个结果,请求出这些结果中大于m的有多少个。
解:字典树,比较一棵树等于比较多个子串,大大减少了冗余比较,在字典树节点中记录。分支中0,1的数量,对于每个串先比较再插入。
import java.util.Scanner;
public class Main{
static class Trie{
Trie[] chi=new Trie[2];
int[] cnt=new int[2];
}
public static void main(String[] arg){
Trie root=new Trie();
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
long count=0;
while(sc.hasNext()){
int num=sc.nextInt();
Trie cur=root;
for(int i=20;cur!=null&&i>=0;i--){
int a=1&(m>>i);
int b=1&(num>>i);
if(a==0&&b==0) count+=cur.cnt[1];
if(a==0&&b==1) count+=cur.cnt[0];
cur=cur.chi[a^b];//b^x=a,->b^a=x
}
cur=root;
for(int i=20;i>=0;i--){
int b=1&(num>>i);
if(cur.chi[b]==null) cur.chi[b]=new Trie();
cur.cnt[b]++;
cur=cur.chi[b];
}
}
System.out.println(count);
}
}