zoukankan      html  css  js  c++  java
  • 力扣 哈希算法

    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'。
    
    

    具体描述请查看Leetcode相关网页:https://leetcode-cn.com/problems/number-of-valid-words-for-each-puzzle/

    方法:状态压缩 + 子集 + 哈希位运算

    1. 不用考虑 words[i] 中重复的字符,以及字符的顺序,换句话说就是 “abbbbcd” 与 "aaaaadcb"应该有相同的 hash 值(a, b, c, d)。那么怎么做呢?第一个想到的是用一个 a-z 的字符数组来表示,但是这样太耗时。可以用位运算来做,例如 int A,A 即是一个字符集,它有 32 位,而字符总共就 26 位满足要求。加入一个字符 c 即做如下操作:A |= 1 << c - 'a';将 A 作为 hash 表的 key,然后hash[A]++ 就可以统计有相同 hash 值的 word 的个数了。
    2. 同理对 puzzles[i] 进行同样的处理,例如 puzzles[i] 经过 hash 后得到字符集合 B,那么就遍历 B 的子集 C,判断 C 中是否含有 puzzle[i] 第一个字母字符,如果有就 ans[i] += hash[C],其中 ans[i] 就是在给出的单词列表 words 中可以作为字谜迷面 puzzles[i] 所对应的谜底的单词数目。
      代码如下:
      vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
            // 如果 word 是 puzzles 的谜底,那么 word 的字符集一定是 puzzles 字符集的子集,并且 puzzles 的第一个字母必须出现在 word 字符集中
            int n = words.size();
            int m = puzzles.size();
            vector<int> ans(m);
            unordered_map<int, int> hash;
            for(int i = 0; i < n;i++){
                int t = 0;
                for(char c : words[i]) t |= (1 << c - 'a');
                hash[t]++;
            }
    
            for(int i = 0; i < m;i++){
                int t = 0, k = 1 << puzzles[i][0] - 'a';
                for(char c : puzzles[i]) t |= (1 << c - 'a');
                for(int j = t; j ; j = (j - 1) & t){ // j = (j - 1) & t 得到的是 t 的子集
                    if(j & k) ans[i] += hash[j];
                }
            }
            return ans;
        }
    
    //集合A、B,元素c --> int A,B   c = 0 ~ 31
    //A中插入c
    A |= (1<<c)
    //A中去除c
    A &= ~(1<<c)
    A ^= (1<<c)
    //A B 合并
    A | B
    //判断B是不是A的子集
    return (A&B) == B
    //判断c在不在A里
    return A & (1<<c)
    //lowbit
    return x & (-x);
    //枚举A的全部子集
    for(int i = A; i; i = (i-1) & A)
    {
        //do something
    }
    
  • 相关阅读:
    获取网卡信息
    MVC Razor
    MVC
    Windows 消息
    sql 总结
    学生成绩表 SQL练习题
    oracle与sqlserver的十大区别
    for的冒泡排序练习题
    对于for的一些认识
    穷举
  • 原文地址:https://www.cnblogs.com/Codroc/p/14451270.html
Copyright © 2011-2022 走看看