位运算在判断重复时候的运用

先说核心,怎么使用位运算来检查重复。
一个int类型的数有32位,用二进制来表示就是
00000000000000000000000000000000

11111111111111111111111111111111
是不是很像一个32位的boolean类型的数组

接下来我们在讲一下位运算
|运算:0000|0101 = 0101
&运算:0011&1001 = 0001
^运算:0011^1001 = 1010
~运算:~1100 = 0011

我们怎么使用位运算来表示boolean数组.
先设置int k = 0等同于建了一个32位的boolean数组;
那就是
00000000000000000000000000000000
现在第2位设为true
00000000000000000000000000000100
对于k来说,就是k=k|(1<<2);
现在判断第5位是否为true
00000000000000000000000000000100

00000000000000000000000000100000
进行&运算
那就是:if((k&(1<<5))>0);
理解了上面这些,就可以实战了

这时候给你一串字符串,全部由小写字母组成,现在问你字符串中保存了哪几个不重复的字母。
去重嘛,第一反应是使用set,每个字母都保存到set中,到时候set中有的字母就是答案了。
然后有些人会反应过来使用boolean[]的数组,都是小写字母,所以字母最多26中,建个26长度的boolean[],出现’a’,将第0位置成true,出现’b’,将第1位置成true,等统计完成后,在回数组中查找,为true的位数就是存在。
使用位运算,其实本质是和boolean[]类型一样的。先上代码吧。

public String getCharNum(String s) {
    char[] chars = s.toCharArray();
    int k = 0;
    for (char c : chars) {
		//或运算
        k = k | (1 << (c - 'a'));
    }
    String ans = "";
    for (int i = 0; i < 26; i++) {
		//且运算
        if ((k & (1 << i)) != 0) {
            ans += (char) (i + 'a');
        }
    }
    return ans;
}

所以在boolean[]数组长度较短时,是可以保存到一个int或者long中去的。

来现在回到面试题 01.01. 判定字符是否唯一,当然,这道题按照要求是不能使用位运算的,毕竟建一个long也是算使用额外的数据结构了。
我们暂时不考虑这个,在不建立数组或者集合的情况下,实现这道题。
这里的特别之处是,char类型是128位,一个long类型64位,不够啊,那就用两个long

public boolean isUnique(String astr) {
    long left = 0;
    long right = 0;
    for (int i = 0; i < astr.length(); i++) {
        if (astr.charAt(i) >= 64) {
            if ((left & (1 << (astr.charAt(i) - 64))) != 0) {
                return false;
            }
            left += (1 << (astr.charAt(i) - 64));//这一步和left |= (1 << (astr.charAt(i) - 64));本质上是一样的
        } else {
            if ((right & (1 << astr.charAt(i))) != 0) {
                return false;
            }
            right += (1 << astr.charAt(i));//这一步和left |= (1 << astr.charAt(i));本质上是一样的
        }
    }
    return true;
}

基本上可以说,能用较短boolean数组实现的,都可以使用位运算,但是可以使用位运算的,使用boolean数组不一定管用。
1178. 猜字谜,这道题,我就是使用了位运算,才能在不超时的情况下完成。

Map<Integer, Integer> scoreMap = new HashMap<Integer, Integer>();

public List<Integer> findNumOfValidWords(String[] words, String[] puzzles) {
	for (int i = 0; i < words.length; i++) {
		//把字符串变成一个int类型
		int score = getScore(words[i]);
		int value = 1;
		//把相同的放在一起
		if (scoreMap.containsKey(score)) {
			value += scoreMap.get(score);
		}
		scoreMap.put(score, value);
	}
	List<Integer> res = new ArrayList<Integer>();
	for (int i = 0; i < puzzles.length; i++) {
		res.add(getAns(puzzles[i]));
	}
	return res;
}

//这个操作就是字符串转int(题目很明显,word中单词的个数是无所谓的,只有有就行)
private int getScore(String word) {
	int ans = 0;
	for (int i = 0; i < word.length(); i++) {
		ans |= 1 << (word.charAt(i) - 'a');
	}
	return ans;
}

private int getAns(String puzz) {
	int ans = 0;
	int key = 1 << (puzz.charAt(0) - 'a');
	//能想到这一步就比较复杂了,puzzles[i].length == 7给了很好的提示,那就是把所有的情况都考虑起来,总共的可能性也就2的6次方种
	//这时候的i就相当于一个6位的boolean类型的数组
	for (int i = 0; i < (1 << (puzz.length() - 1)); i++) {
		//因为必须包含首字母,所以key必须加上
		int k = key;
		for (int j = 1; j < puzz.length(); j++) {
			//使用位运算,来判断,当前数字是否该算上
			if ((i & (1 << (j - 1))) != 0) {
				k += (1 << (puzz.charAt(j) - 'a'));
			}
		}
		//答案中时包含k的,那就加上
		if (scoreMap.containsKey(k)) {
			ans += scoreMap.get(k);
		}
	}
	return ans;
}
发布了148 篇原创文章 · 获赞 18 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_33321609/article/details/104539143