ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LeetCode 每日一题1178. 猜字谜

2021-02-26 14:01:25  阅读:241  来源: 互联网

标签:puzzles 谜底 bitnum 1178 单词 谜面 猜字谜 words LeetCode


1178. 猜字谜

外国友人仿照中国字谜设计了一个英文版猜字谜小游戏,请你来猜猜看吧。

字谜的迷面 puzzle 按字符串形式给出,如果一个单词 word 符合下面两个条件,那么它就可以算作谜底:

  • 单词 word 中包含谜面 puzzle 的第一个字母。
  • 单词 word 中的每一个字母都可以在谜面 puzzle 中找到。

例如,如果字谜的谜面是 "abcdefg",那么可以作为谜底的单词有 "faced", "cabbage", 和 "baggage";而 "beefed"(不含字母 "a")以及 "based"(其中的 "s" 没有出现在谜面中)。

返回一个答案数组 answer,数组中的每个元素 answer[i] 是在给出的单词列表 words 中可以作为字谜迷面 puzzles[i] 所对应的谜底的单词数目。

示例:

输入:
words = ["aaaa","asas","able","ability","actt","actor","access"], 
puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"]
输出:
[1,1,3,2,4,0]
解释:
1 个单词可以作为 "aboveyz" 的谜底 : "aaaa" 
1 个单词可以作为 "abrodyz" 的谜底 : "aaaa"
3 个单词可以作为 "abslute" 的谜底 : "aaaa", "asas", "able"
2 个单词可以作为 "absoryz" 的谜底 : "aaaa", "asas"
4 个单词可以作为 "actresz" 的谜底 : "aaaa", "asas", "actt", "access"
没有单词可以作为 "gaswxyz" 的谜底,因为列表中的单词都不含字母 'g'。

提示:

  • 1 <= words.length <= 10^5
  • 4 <= words[i].length <= 50
  • 1 <= puzzles.length <= 10^4
  • puzzles[i].length == 7
  • words[i][j], puzzles[i][j] 都是小写英文字母。
  • 每个 puzzles[i] 所包含的字符都不重复。

方法一:状态压缩 + 枚举

解题思路

此题的暴力解法不难想到,对于每一个谜面 puzzle 都去遍历 words 求解。
根据提示:谜面最大值 10^4、谜底最大值 10^5,相乘为 10^9,一定会超时。

所以,需要思考其他解法:

  • 根据提示,谜面和谜底都只包含小写字母,且我们并不关心重复的字母。对于任一谜面或谜底,我们只关心某个字母是否出现过,这样我们可以把字符串用 26bit 来表示,从左往右分别表示字母 za,值为 0 表示未出现,1 表示出现过。
  • Java 中 int 有 32 位,可以直接使用 int 的后 26 位来表示字符串。

理解了上述压缩过程,我们来看具体的实现步骤:

  • 先把谜底 words 中的字符串转为数字,不同的 word 可能转成相同的数字,如 “ab”“aaabbb” 转成数字都是 3。因此,需要一个 map 统计每个数字的频率。
  • 遍历谜面 puzzles,同样的把谜面转成数字。根据提示谜面只有 7 位,也就是转成的数字的二进制位只有 7 个 1。我们可以 枚举 所有子集(27 - 1),对于任一子集只要 包含首字母且 map 中存在 就是有效的。

细节:

  • subnum = (subnum - 1) & bitnum 用于枚举 bitnum 所有子集,可以试着画图理解这句代码。

参考代码

public List<Integer> findNumOfValidWords(String[] words, String[] puzzles) {
	// 把 words 中的字符串转为数字,并统计数字的出现次数。
    Map<Integer, Integer> freq = new HashMap<>();
    for (String word : words) {
        int bitnum = getBitnum(word);
        freq.put(bitnum, freq.getOrDefault(bitnum, 0) + 1);
    }
	
    List<Integer> ans = new ArrayList<>();
    for (String puzzle : puzzles) {
        int bitnum = getBitnum(puzzle);
        int first = 1 << (puzzle.charAt(0) - 'a');
        int subnum = bitnum;
        int count = 0;
        // 枚举谜面的所有子集:包含首字母 && map中存在
        while (subnum > 0) {
            if ((subnum & first) > 0 && freq.containsKey(subnum)) {
                count += freq.get(subnum);
            }
            subnum = (subnum - 1) & bitnum;
        }
        ans.add(count);
    }
    return ans;
}
public Integer getBitnum(String s) {
    int bitnum = 0;
    for (char ch : s.toCharArray()) {
        bitnum |= (1 << (ch - 'a'));
    }
    return bitnum;
}

执行结果
在这里插入图片描述

标签:puzzles,谜底,bitnum,1178,单词,谜面,猜字谜,words,LeetCode
来源: https://blog.csdn.net/qq_27007509/article/details/114125796

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有