zoukankan      html  css  js  c++  java
  • LeetCode1160-拼写单词

    题目描述

    给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
    假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我们就认为你掌握了这个单词。
    注意:每次拼写时,chars 中的每个字母都只能用一次。
    返回词汇表 words 中你掌握的所有单词的 长度之和。

    示例:
    输入:words = ["hello","world","leetcode"], chars = "welldonehoneyr"
    输出:10
    解释:
    可以形成字符串 "hello" 和 "world",所以答案是 5 + 5 = 10。

    我的题解

    对于这题,有多个Word,需要使用chars来检验这个Word是否被‘掌握’。
    重点是如何检验?
    如果Word包含chars的每一个字母就‘掌握’,否则‘未掌握’
    如何判断包含与否?
    一个较好的做法是:把chars中每一个字母出现的次数计算出来,用arr存储起来,然后对于每一个Word,遍历其字母,每次将arr中的次数减1,如果为减后为负数(减之前为0),就表示chars中不存在Word的这个字母,为‘未掌握’。

    上菜:

     public int countCharacters(String[] words, String chars) {
            int [] [] arr = new int[123][1]; //使用一个二维数组记录chars中每一个字母出现的次数,97-122对应a-z。这里避免后面不断减97,牺牲空间换取时间,前97个数组单元无用。
            for (char ch: chars.toCharArray()){
                arr[ch][0]++;//初始化chars中每个char出现的次数
            }
            int sum =0;//计数:掌握的所有单词的 长度之和
            int [][]tmp = new int[123][1];//每一个Word都需要判断,于是这是arr的copy
            for (String word : words){
                for (int i=97;i<123;i++){//从arr备份一份过来
                    tmp[i][0]=arr[i][0];
                }
                boolean match =true;//是否匹配
                for (char ch:word.toCharArray()){//遍历每一个Word的字母
                    if(tmp[ch][0]--==0){//每次使用该字母作为下标,给数组减1,减之前=0即表示字母已经被用完了,不可能匹配
                        match = false;
                        break;
                    }
                }
                if (match) sum+=word.length();//加上Word长度
            }
            return sum;
        }
    

    复杂度分析:

    执行用时 :7 ms, 在所有 Java 提交中击败了84.18%的用户
    内存消耗 :41.5 MB, 在所有 Java 提交中击败了5.08%的用户

    1. 时间复杂度:O(m x n),m是words数组长度,n是chars长度
    2. 空间复杂度:O(1),使用了两个123X1的二维数组

    优化:

    想了一下:
    为什么要使用两个123X1二维数组,使用一个 123X2 的数组它不好吗?
    第二维中第一个单元用作原始计数,第二个单元用作备份,每次重置,用于每一个Word的检验

        public int countCharacters(String[] words, String chars) {
            int [] [] arr = new int[123][2]; //使用一个二维数组记录chars中每一个字母出现的次数,97-122对应a-z。这里避免后面不断减97,牺牲空间换取时间,前97个数组单元无用。
            for (char ch: chars.toCharArray()){
                arr[ch][0]++;//初始化
            }
            int sum =0;//计数:掌握的所有单词的 长度之和
            for (String word : words){
                for (int i=97;i<123;i++){//从arr导入过来
                    arr[i][1]=arr[i][0];
                }
                boolean match =true;//是否匹配
                for (char ch:word.toCharArray()){//遍历每一个Word的字母
                    if(arr[ch][1]--==0){//每次使用该字母作为下标,给数组减1,减之前<=0即表示字母已经被用完了,不可能匹配
                        match = false;
                        break;
                    }
                }
                if (match) sum+=word.length();//加上Word长度
            }
            return sum;
        }
    

    这样一改,时间空间上都有提升:
    执行用时 :6 ms, 在所有 Java 提交中击败了89.17%的用户
    内存消耗 :41.7 MB, 在所有 Java 提交中击败了5.08%的用户

    其他题解

    官方题解

    显然,对于一个单词 word,只要其中的每个字母的数量都不大于 chars 中对应的字母的数量,那么就可以用 chars 中的字母拼写出 word。所以我们只需要用一个哈希表存储 chars 中每个字母的数量,再用一个哈希表存储 word 中每个字母的数量,最后将这两个哈希表的键值对逐一进行比较即可。

    # Python
    class Solution:
        def countCharacters(self, words: List[str], chars: str) -> int:
            chars_cnt = collections.Counter(chars)
            ans = 0
            for word in words:
                word_cnt = collections.Counter(word)
                for c in word_cnt:
                    if chars_cnt[c] < word_cnt[c]:
                        break
                else:
                    ans += len(word)
            return ans
    

    我的思路和它这个相似
    官方解法不可避免地为每一个Word计算字母出现次数
    我的不用,但是我的解法需要每次复制一份chars字母出现次数用于检验

  • 相关阅读:
    jQuery自定义选项卡插件
    jQuery委托事件delegate()方法
    发布/订阅模式
    Node.js + Nginx WNMP 多域名 多端口 反向代理
    让Nginx支持apk、ipa文件下载
    jQuery中bind方法传参
    Http协议详解
    vuecli2.X环境搭建
    vue绑定属性、绑定class及绑定style
    vue数据渲染、条件判断及列表循环
  • 原文地址:https://www.cnblogs.com/XT-xutao/p/12514690.html
Copyright © 2011-2022 走看看