题目:
给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。
示例 1:
输入: words = ["abcw","baz","foo","bar","fxyz","abcdef"]
输出: 16
解释: 这两个单词为 "abcw", "fxyz"。它们不包含相同字符,且长度的乘积最大。
示例 2:
输入: words = ["a","ab","abc","d","cd","bcd","abcd"]
输出: 4
解释: 这两个单词为 "ab", "cd"。
提示:
2 <= words.length <= 1000
1 <= words[i].length <= 1000
words[i] 仅包含小写字母
分析:
-
【假设字符串中只包含英语的小写字母】
—这里指明字符串没有大写,都属于小写
-
【如果没有不包含相同字符的一对字符串】
—如果算法结束依旧没有寻找到差异字符串,那么有题目提前设计好的答案,返回即可
-
【2 <= words.length <= 1000】【1 <= words[i].length <= 1000】
—此两条信息只是为了简单说明字符串数组(大于2,才可能出现比较)情况及各个字符串的情况。
代码:
第一版:
第一想法应该是觉得需要整体遍历,且不重复比较。比较每两个字符串是否存在一致,如果不存在那么记录最大乘积值,如果存在那么继续过滤,等算法结束后返回遍历过程中可以取得的最大值即可。
代码:
class Solution {
public int maxProduct(String[] words) {
return first01(words);
}
public static int first01(String[] words){
//初始化变量:len 字符串数组长度 max_res:负责记录遍历过程中出现最大的不同单词成乘积值
int len = words.length;
int max_res = 0;
//双重循环
for(int i=0;i<len;i++){
for(int j=i+1;j<len;j++){
//获取当前遍历过程中的 两个单词
String str1 = words[i];
String str2 = words[j];
//定义标志位,如果遍历过程中,单词与单词出现了重复,那么标志=false,否则依旧为true
boolean flag = true;
//任意选中一个单词,拆分字母,分别在另外一个单词中寻找是否重复
for(int k=0;k<str2.length();k++){
if(str1.indexOf(str2.charAt(k))!=-1){
flag = false; // 如果重复,修改标志,并且退出这两个单词的比较 进入下一轮
break;
}
}
//如果这次循环中,两个单词没有重复的,那么计算乘积值,并且与max_res判断是否需要更新
if(flag){
max_res = Math.max(max_res,str1.length()*str2.length());
}
}
}
//返回结果
return max_res;
}
}
第二版:
对比单词是否出现重复元素,双重循环我是无法再做优化了,但是在单词对比这块上,还继续存在优化的点。
假设给定一个单词“abd” 那么可以使用int的二进制来表示这个单词的组成:
0000 0000 0000 0000 0000 0000 0000 1011
【第一位=1代表存在a,第二位=1代表存在b,第三位=0代表不存在c,第四位=0代表存在d】
是不是清晰明了?
再假设有两个单词
”afh“ --> 0000 0000 0000 0000 0000 0000 1010 0001
”fh“ --> 0000 0000 0000 0000 0000 0000 1010 0000
如果两个单词之间不存在重复元素,那么也就是意味着 两个单词转化后的二进制对应为一定是【0,0】或者【0,1】
如:“abd” 和 ”fh“ ,对应为要么都是0,要么是[0,1]
所以,可以按照规律来实现单词是否重复逻辑:
a&b = 0
===> (a b 元素不重复)
代码:
class Solution {
public int maxProduct(String[] words) {
return first02(words);
}
//既然需要一一循环,那么就提高判断是否包含的方法效率
public static int first02(String[] words){
int len = words.length;
int max_res = 0;
int[] wordsBinaryArr = initArr(words);
for(int i=0;i<len;i++){
for(int j=i+1;j<len;j++){
//两个单词的判断是否重合[如果完全不重合,那么 & 的结果应该是0]
if( (wordsBinaryArr[i] & wordsBinaryArr[j]) == 0 ){
max_res = Math.max(max_res,(words[i].length() * words[j].length()));
}
}
}
return max_res;
}
//为每一个单词都生成一个二进制表示
public static int[] initArr(String[] words){
//一共几个单词就创建几个大小的二进制表示数组
int[] wordsBinaryArr = new int[words.length];
int k = 0;
for(String str:words){
//创建临时变量j,用来作为当前遍历过程中的str二进制表示
int j = 0;
for(int i=0;i<str.length();i++){
//都是小写,所以直接减'a'即可 ==> 得出单词在二进制中表示的位置 如果charAt(i)='a' 那么'a'-'a' 说明a就是第0个位置
//取得位置之后,需要把对应位置的0-->1,所以这里需要 1<< (str.charAt(i)-'a')
//这里使用 '|' 是只要存在就是1,避免出现2次加两次1导致 进位成0
j |= (1<< (str.charAt(i)-'a'));
}
//该单词对应的二进制表示就是 j ,和words中单词的下标索引一致
wordsBinaryArr[k++] = j;
}
//返回二进制数组表示
return wordsBinaryArr;
}
}
总结:
这道题的升华点在于,使用二进制来表示一个单词中元素的存在与否,进而提高单词匹配效率。高!确实是高!
大家好,我是二十一画,感谢您的品读,如有帮助,不胜荣幸~😊