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字母出现次数用于检验

posted @ 2020-03-17 23:50  Edwin_Xu  阅读(204)  评论(0编辑  收藏  举报