单词长度的最大乘积

  1. 链接
    https://leetcode.cn/problems/aseY1I/

  2. 题目
    给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。

示例
输入: words = [“abcw”,“baz”,“foo”,“bar”,“fxyz”,“abcdef”]
输出: 16
解释: 这两个单词为 “abcw”, “fxyz”。它们不包含相同字符,且长度的乘积最大。

  1. 思路

    1. 第一种,暴力循环破解法。使用map来记录字母的出现,对数组进行循环遍历一一对比,如果出现重复字母,则跳过该轮两个单词的对比;否则计算单词长度乘积,并与ans对比,选出最大者。该方法的时间和内存消耗都很惊人。
    2. 第二种,位运算法。相较于第一种方法,本方法是对判断字符是否有公共字母的方法进行了优化。第一种是使用map来一一比,看是否有重复值。本方法是把单词转变为一个掩码,掩码的每一位代表一个字母,如果该字母存在则是1,不存在则是0。
      根据题意,单词只包含小写字母,共有26个字母,所以26位掩码来分别表示每个字母是否在单词中出现。当每个单词都有自己的掩码之后,再进行循环进行一一判断,当两个单词的掩码进行&运算后,如果为0则表示没有公共字母,则可以计算这两个单词的长度乘积。
    3. 第三种,位运算优化法。本方法的核心思路与第二种方法一致,但是第二种方法是每一个单词记录一个掩码,再进行循环对比。而本方法是使用map存下掩码对应的最长长度值,再进行一一对比。比如,aaaaaa,这两个的单词掩码是相等的,如果按照第二种方法,则是这两个单词对应两个掩码,且这两个掩码相等,循环时会做重复比较。而第二种方法,则是只存一个掩码,并把长度记录为aaaaa的长度,这样循环时会减去一些对比。
  2. 代码

class Solution {
public:
    int maxProduct(vector<string>& words) {
        //第一种,暴力循环破解,时间消耗和内存消耗惊人
        int ans=0,tmp,k=0,l;
        //ans记录最大值,tmp用在循环里,表示两个单词是否有公共字母,k用于索引,l记录长度
        map<char,int> re;
        //map记录字母是否在单词中是否出现
        for(int i=0;i<words.size();i++){
            while(words[i][k]!='\0')
            re[words[i][k++]]++;
            l=k;
            //记录固定单词的字母出现已经长度
            k=0;
        for(int j=i+1;j<words.size();j++){
        //把固定单词与余下的每个单词进行比较
            while(words[j][k]!='\0')
            if(re[words[j][k++]]!=0){
            tmp=-1;
            break;
            //如果该字母与固定单词有公共字母,则tmp记为-1,且跳过与该单词的后续比较
            }
            if(tmp==-1)
            tmp=0;
            else
            tmp=l*k;
            ans=ans>=tmp?ans:tmp;
            tmp=0;
            k=0;
            //tmp和k都要记得还原
        }
        re.clear();
        }
        return ans;

        //第二种,位运算法,对于判断字符是否有公共字母的方法进行优化
        int ans=0,len=words.size();
        vector<int> mask(len);
        //vector来记录每个单词的掩码
        for(int i=0;i<len;i++)
        for(int j=0;j<words[i].size();j++)
        mask[i]|=1<<(words[i][j]-'a');
        //x=words[i][j]-'a'得到0-25的数值,比如'a'-'a'为0,'c'-'a'=2,下面以words[i][j]='c'为例
        //1<<x,即1左移x位,左移填0,得到100
        //再与mask进行位或运算
        //该单词所有字母循环结束后,就会得到一个对应的掩码。比如”add“为1001,即低位1代表a,高位1代表d,表面该单词存在ad两个字母

        for(int i=0;i<len;i++)
        for(int j=i+1;j<len;j++)
        //(mask1&mask2)一定要加小括号,不加,return只为0
        //==的优先级高于&,不叫括号,会先进行mask2==0的运算,由于mask2不会为0,所以此步运算得出0;再进行mask1&0的运算,得0
        if((mask[i]&mask[j])==0)
        ans=max(ans,int(words[i].size()*words[j].size()));
        return ans;

        //第三种,针对位运算法的优化,让mask唯一化。比如met和meet,按照第二种方法,二者的mask一样且都存入数组中,但按照下面这种方法,mask只存一次,记录最长的单词长度,即meet的长度
        int ans=0,len=words.size();
        unordered_map<int,int> map;
        for(int i=0;i<len;i++){
            int l2=words[i].size();
            int mask=0;
            for(int j=0;j<l2;j++){
                mask|=1<<(words[i][j]-'a');
            }
            if(map.count(mask)){
                if(l2>map[mask])
                map[mask]=l2;
            }else{
                map[mask]=l2;
            }
        }
        for(auto[mask1, _]:map){
            int l1=map[mask1];
            for(auto[mask2, _]:map){
                int l3=map[mask2];
                if((mask1&mask2)==0){
                    ans=max(ans,l1*l3);
                }
            }
        }
        return ans;   
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值