单词长度的最大乘积 | 循序递进---@二十一画

题目:

给定一个字符串数组 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] 仅包含小写字母

分析:

  1. 【假设字符串中只包含英语的小写字母】

    —这里指明字符串没有大写,都属于小写

  2. 【如果没有不包含相同字符的一对字符串】

    —如果算法结束依旧没有寻找到差异字符串,那么有题目提前设计好的答案,返回即可

  3. 【2 <= words.length <= 1000】【1 <= words[i].length <= 1000】

    —此两条信息只是为了简单说明字符串数组(大于2,才可能出现比较)情况及各个字符串的情况。

代码:

第一版:

第一想法应该是觉得需要整体遍历,且不重复比较。比较每两个字符串是否存在一致,如果不存在那么记录最大乘积值,如果存在那么继续过滤,等算法结束后返回遍历过程中可以取得的最大值即可。
image.png

代码:
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 元素不重复)
image.png

代码:
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;

    }
}

总结:

这道题的升华点在于,使用二进制来表示一个单词中元素的存在与否,进而提高单词匹配效率。高!确实是高!

大家好,我是二十一画,感谢您的品读,如有帮助,不胜荣幸~😊

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值