leetcode 1178.猜字谜

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] 所包含的字符都不重复。

题解

直接模拟,用数组映射26个字母的出现次数,显然超时了

class Solution {
public:
    vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
        int puzzleCount = puzzles.size();
        int wordCount = words.size();
        vector<vector<int> > puzzleStat(puzzleCount,vector<int>(26,0));
        vector<vector<int> > wordStat(wordCount,vector<int>(26,0));
        vector<int> ans(puzzleCount); 
        for(int i = 0 ; i < puzzleCount ; ++i){
            for(auto j : puzzles[i]){
                puzzleStat[i][j - 'a']++;
            }
        }
        for(int i = 0 ; i < wordCount ; ++i){
            for(auto j : words[i]){
                wordStat[i][j - 'a']++;
            }
        }

        for(int i = 0 ; i < puzzleCount ; ++i){
            int ansCount = 0;
            for(int j = 0 ; j < wordCount ; ++j){
                //如果j标words中有谜面的首字母
                if(wordStat[j][puzzles[i][0] - 'a'] != 0){
                    bool flag = true;
                    for(int k = 0 ; k < 26 ; ++k){
                        //如果j标words中存在的字母在谜面中bu存在
                        if(wordStat[j][k] != 0 && puzzleStat[i][k] == 0){
                            flag = false;
                            break;
                        }
                    }
                    if(flag){
                        ansCount++;
                    }
                }
            }
            ans[i] = ansCount;
        }
        return ans;
    }
};

思考一下位运算的可能性,2^26 = 67108864 < INT_MAX
那好办,就不用数组映射字母出现次数了,用一个二进制int来映射就行,因为题目要求的判别并不用判断字母的个数,只要能够表示是否存在就可以了
还就那个继续超时

class Solution {
public:
    //获取num从右往左第n位
    int getDigit(int num,int n){
        num >>= n;
        return num & 1;
    }

    vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
        int puzzleCount = puzzles.size();
        int wordCount = words.size();
        vector<int> puzzleStat(puzzleCount,0);
        vector<int> wordStat(wordCount,0);
        vector<int> ans(puzzleCount); 
        for(int i = 0 ; i < puzzleCount ; ++i){
            int tempCount = 0;
            for(auto j : puzzles[i]){
                puzzleStat[i] |= 1 << (j - 'a');
            }
        }
        for(int i = 0 ; i < wordCount ; ++i){
            for(auto j : words[i]){
                wordStat[i] |= 1 << (j - 'a');
            }
        }

        for(int i = 0 ; i < puzzleCount ; ++i){
            int ansCount = 0;
            for(int j = 0 ; j < wordCount ; ++j){
                //如果j标words中有谜面的首字母
                //out<<getDigit(wordStat[j],puzzles[i][0] - 'a')<<endl;
                if(getDigit(wordStat[j],puzzles[i][0] - 'a') != 0){
                    //如果words中存在的字母谜面中不存在
                    //cout<<wordStat[j]<<' '<<puzzleStat[i]<<' '<<(wordStat[j] & puzzleStat[i])<<endl;
                    if((wordStat[j] & puzzleStat[i]) == wordStat[j]){
                        ansCount++;
                    }
                }
            }
            ans[i] = ansCount;
        }
        return ans;
    }
};

那就只能反过来遍历了,根据谜面枚举谜面的所有二进制子集(因为谜面只有7位),然后把words对应的二进制信息放到哈希表中,通过谜面二进制的子集元素来检查哈希表

class Solution {
public:
    vector<int> findNumOfValidWords(vector<string>& words, vector<string>& puzzles) {
        int puzzleCount = puzzles.size();
        int wordCount = words.size();
        unordered_map<int,int> wordsMask;
        vector<int> ans(puzzleCount,0); 
        for(int i = 0 ; i < wordCount ; ++i){
            int tempMask = 0;
            for(auto j : words[i]){
                tempMask |= 1 << (j - 'a');
            }
            if (__builtin_popcount(tempMask) <= 7) {
                wordsMask[tempMask]++;
            }
        }

        for(int i = 0 ; i < puzzleCount ; ++i){
            int sum = 0;
            int puzzlesMask = 0;
            for(auto j : puzzles[i]){
                puzzlesMask |= 1 << (j - 'a');
            }

            int tempMask = puzzlesMask;
            do{
                int index = tempMask | (1 << (puzzles[i][0] - 'a') );
                if(wordsMask.count(index)){
                    sum += wordsMask[index];
                }
                tempMask = (tempMask - 1) & puzzlesMask;
            }while(tempMask != puzzlesMask);
            ans[i] = sum / 2;
        }
        return ans;
    }
};

关键还就是这手二进制子集遍历:

int tempMask = puzzlesMask;
            do{
                int index = tempMask | (1 << (puzzles[i][0] - 'a') );
                if(wordsMask.count(index)){
                    sum += wordsMask[index];
                }
                tempMask = (tempMask - 1) & puzzlesMask;
            }while(tempMask != puzzlesMask);

有点写呲了,这里我的写法把整个谜面取进了mask,导致在后面遍历子集的时候连带着首位的1在遍历,结果就是统计个数翻了一番。

            for(auto j : puzzles[i]){
                puzzlesMask |= 1 << (j - 'a');
            }

这里如果改写成下标,不取第一位的话,后面sum就不用/2了

			for(int j = 1 ; j < 7 ; ++j){
                puzzlesMask |= 1 << (puzzles[i][j] - 'a');
            }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值