原题地址:剑指 Offer II 005. 单词长度的最大乘积
给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。
直接遍历
public int maxProduct(String[] words) {
int res = 0;
int n = words.length;
for (int i = 0; i < n; i++) {
String A = words[i];
for (int j = 0; j < n; j++) {
String B = words[j];
if (!helpWord(A, B)) res = Math.max(res, (A.length() * B.length()));
}
}
return res;
}
// 两个字符串之间是否存在公共字符
public boolean helpWord(String A, String B) {
for (char c : A.toCharArray()) {
// indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置
// 如果没有找到匹配的字符串则返回 -1
if (B.indexOf(c) != -1) return true;
}
return false;
}
此方法为暴力解法,直接遍历比对两个字符串之间是否存在相同的公共字符。leetcode的用例此方法会超时
位运算
public int maxProduct(String[] words) {
int n = words.length;
// 存储将原本的数组转为二进制后的新数组
// 将字符串中字符出现情况用二进制数表示,即字符a~z对应二进制数第0~25位,
// 哪个字符出现就将二进制数对应位置1。这样的话对于不含相同字符的字符串,
// 它们的二进制表示进行与运算结果就为0,否则结果就不为0,位运算能大大加快算法速度。
int[] binaryBit = new int[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < words[i].length(); j++) {
// binaryBit[i]用数字的二进制表示来记录words[i]包含的字母
// 有字母则其在字母表对应位的数字二进制该位置为1,不存在的字母为0。
binaryBit[i] |= (1 << (words[i].charAt(j) - 'a'));
}
}
int res = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// 如果i、j单词对应的二进制某一位有都为1,则说明该位置表示的字母二者都有
// 否则若binaryBit[i]&binaryBit[j]==0,说明两个单词没有重复字母,可以计算乘积。
// 与运算中:1&1=1
if ((binaryBit[i] & binaryBit[j]) == 0) res = Math.max(res, words[i].length() * words[j].length());
}
}
return res;
}
将原来的每个单词转换为二进制,单词对应的二进制中,某一位为0表示这一位的字母没有出现,为1则表示出现。然后通过对两个单词的二进制进行位运算(位运算中只有1&1=1)可以快速区别两个单词之间是否存在公共字符,节省时间效率。