LeetCode 717. 1 比特与 2 比特字符 / 838. 推多米诺 / 1994. 好子集的数目(状态压缩动态规划)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 717. 1 比特与 2 比特字符 / 838. 推多米诺 / 1994. 好子集的数目(状态压缩动态规划)相关的知识,希望对你有一定的参考价值。

717. 1 比特与 2 比特字符

2022.2.20 每日一题,来学校忘了打卡了,快三百天断了,我…

题目描述

有两种特殊字符:

第一种字符可以用一比特 0 表示
第二种字符可以用两比特(10 或 11)表示

给你一个以 0 结尾的二进制数组 bits ,如果最后一个字符必须是一个一比特字符,则返回 true 。

示例 1:

输入: bits = [1, 0, 0]
输出: true
解释: 唯一的解码方式是将其解析为一个两比特字符和一个一比特字符。
所以最后一个字符是一比特字符。

示例 2:

输入:bits = [1,1,1,0]
输出:false
解释:唯一的解码方式是将其解析为两比特字符和两比特字符。
所以最后一个字符不是一比特字符。

提示:

1 <= bits.length <= 1000
bits[i] 为 0 或 1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/1-bit-and-2-bit-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution 
    public boolean isOneBitCharacter(int[] bits) 
        //碰到1就是两比特
        int l = bits.length;
        for(int i = 0; i < l;)
            if(i == l - 1)
                return true;
            if(bits[i] == 0)
                i++;
            else
                i += 2;
            
        
        return false;
    

838. 推多米诺

2022.2.21 每日一题

题目描述

n 张多米诺骨牌排成一行,将每张多米诺骨牌垂直竖立。在开始时,同时把一些多米诺骨牌向左或向右推。

每过一秒,倒向左边的多米诺骨牌会推动其左侧相邻的多米诺骨牌。同样地,倒向右边的多米诺骨牌也会推动竖立在其右侧的相邻多米诺骨牌。

如果一张垂直竖立的多米诺骨牌的两侧同时有多米诺骨牌倒下时,由于受力平衡, 该骨牌仍然保持不变。

就这个问题而言,我们会认为一张正在倒下的多米诺骨牌不会对其它正在倒下或已经倒下的多米诺骨牌施加额外的力。

给你一个字符串 dominoes 表示这一行多米诺骨牌的初始状态,其中:

dominoes[i] = ‘L’,表示第 i 张多米诺骨牌被推向左侧,
dominoes[i] = ‘R’,表示第 i 张多米诺骨牌被推向右侧,
dominoes[i] = ‘.’,表示没有推动第 i 张多米诺骨牌。

返回表示最终状态的字符串。

示例 1:

输入:dominoes = “RR.L”
输出:“RR.L”
解释:第一张多米诺骨牌没有给第二张施加额外的力。

示例 2:


输入:dominoes = “.L.R…LR…L…”
输出:“LL.RR.LLRRLL…”

提示:

n == dominoes.length
1 <= n <= 10^5
dominoes[i] 为 ‘L’、‘R’ 或 ‘.’

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/push-dominoes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

模拟这个过程,如果上一个位置的牌和当前位置的牌朝向相同,那么就往一边倒下去,否则向中间倒下去

class Solution 
    public String pushDominoes(String dominoes) 
        //得记录每个推倒的牌的位置,L和R个统计一个list
        //然后从左到右遍历这两个list,找到一个left最近的right
        //然后就能把这中间的牌推倒
        //如果没有,就往左推;如果right过后还是right,就往右推

        int l = dominoes.length();
        
        int left = -1;
        int right = -1;
        
        char[] cc = new char[l];
        char lastc = '.';
        int last = 0;
        Arrays.fill(cc, '.');

        for(int i = 0; i < l; i++)
            char c = dominoes.charAt(i);
            if(c == 'L')
                left = i;
                //如果上一个是R,就往中间倒下
                if(lastc == 'R')
                    while(right < left)
                        cc[right++] = 'R';
                        cc[left--] = 'L';
                    
                //如果是别的,就往左倒下去
                else
                    while(last <= left)
                        cc[last++] = 'L';
                    
                
                lastc = 'L';
                last = i;
            else if(c == 'R')
                right = i;
                if(lastc == 'R')
                    while(last <= right)
                        cc[last++] = 'R';
                    
                
                lastc= 'R';
                last = i;
            
        
        if(lastc == 'R')
            while(last < l)
                cc[last++] = 'R';
            
        
        return new String(cc);
    

1994. 好子集的数目

2022.2.22 每日一题,听说今天是最有爱,爱最多的一天

题目描述

给你一个整数数组 nums 。如果 nums 的一个子集中,所有元素的乘积可以表示为一个或多个 互不相同的质数 的乘积,那么我们称它为 好子集 。

比方说,如果 nums = [1, 2, 3, 4] :
[2, 3] ,[1, 2, 3] 和 [1, 3] 是 好 子集,乘积分别为 6 = 2 * 3 ,6 = 2 * 3 和 3 = 3 。
[1, 4] 和 [4] 不是 好 子集,因为乘积分别为 4 = 2 * 2 和 4 = 2 * 2 。

请你返回 nums 中不同的 好 子集的数目对 10^9 + 7 取余 的结果。

nums 中的 子集 是通过删除 nums 中一些(可能一个都不删除,也可能全部都删除)元素后剩余元素组成的数组。如果两个子集删除的下标不同,那么它们被视为不同的子集。

示例 1:

输入:nums = [1,2,3,4]
输出:6
解释:好子集为:
-[1,2]:乘积为 2 ,可以表示为质数 2 的乘积。
-[1,2,3]:乘积为 6 ,可以表示为互不相同的质数 2 和 3 的乘积。
-[1,3]:乘积为 3 ,可以表示为质数 3 的乘积。
-[2]:乘积为 2 ,可以表示为质数 2 的乘积。
-[2,3]:乘积为 6 ,可以表示为互不相同的质数 2 和 3 的乘积。
-[3]:乘积为 3 ,可以表示为质数 3 的乘积。

示例 2:

输入:nums = [4,2,3,15]
输出:5
解释:好子集为:
-[2]:乘积为 2 ,可以表示为质数 2 的乘积。
-[2,3]:乘积为 6 ,可以表示为互不相同质数 2 和 3 的乘积。
-[2,15]:乘积为 30 ,可以表示为互不相同质数 2,3 和 5 的乘积。
-[3]:乘积为 3 ,可以表示为质数 3 的乘积。
-[15]:乘积为 15 ,可以表示为互不相同质数 3 和 5 的乘积。

提示:

1 <= nums.length <= 10^5
1 <= nums[i] <= 30

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/the-number-of-good-subsets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

这个题呢,关键是看到只有1到30 三十种数
这三十种数中有10个质数,1个1
剩下的数中,还有那种有两个相同质数乘积的因数的数,比如4 8 12 25这种,这种数肯定不能考虑,所以跳过
剩下的合数,例如10 14 这种合数,可以用不同的质数乘积表示,可以考虑

那么遍历除了1以外的29个数,用一个数subset表示这些数可以被哪些质数表示,跳过那些特殊的合数
然后遍历所有mask,如果当前mask与subset有交集,那么说明有两个相同的质数被使用到了,跳过
如果都只有被使用了一次的质数,那么记录这个情况

在遍历这些情况的时候,因为要使用到f[pre]这个值,为了保证这个值没有被覆盖,所以使用倒叙遍历,相或肯定会比pre这值大,所以可以保证这个值在遍历过程中没有被覆盖
如果正序遍历,会导致f[pre]被当前轮次的遍历覆盖掉,导致结果错误

最后呢,就是对1的处理,每有一个1,那么情况数就乘2

class Solution 
    int MOD = (int)1e9 + 7;
    //质数集合
    int[] primes = 2,3,5,7,11,13,17,19,23,29;

    public int numberOfGoodSubsets(int[] nums) 
        //虽然数很多,但是种类只有30种
        //肯定要先统计每种数有多少个
        //然后对30个数进行组合,如果能被互不相同的质数相乘得到,就是好子集
        //30以内的质数,1 2 3 5 7 11 13 17 19 23 29
        //对这些数进行组合,得到的数就是可以被互不相同质数相乘得到的数
        
        //想想比这些数更大的数能用更大的质数组合得到么,好像不行

        //对了,一些合数也能组合,比如第二个例子中的,2,15这个子集
        //但是本身包含4,9,16,25这种合数的不能就行组合,这些数有,4 8 9 12 16 18 20 24 25 27 28
        //那么除了本身是质数的合数就剩下 6 10 14 15 21 22 26 30
        
        //先统计每种数的个数
        int[] count = new int[31];
        
        for(int n : nums)
            count[n]++;
        

        //状态压缩,十个质数的使用状态
        int mask = 1 << 10;

        long[] f = new long[mask];

        //初始化
        f[0] = 1;

        //对于每个数i,看是否能加入到子集中
        for(int i = 2; i <= 30; i++)
            //如果没有这个数,不用考虑
            if(count[i] == 0)
                continue;

            //检查当前数i的质因数,是否超过了一个
            boolean flag = false;
            int subset = 0;
            for(int j = 0; j < 10; j++)
                if(i % (primes[j] * primes[j]) == 0)
                    flag = true;
                    break;
                
                //如果能除尽这个数,把这个位置标志位1
                if(i % primes[j] == 0)
                    subset |= (1 << j);
                
            

            //如果这个数同一个质数超过了1个,那么不能加入子集中
            if(flag)
                continue;

            //考虑所有状态
            for(int pre = mask - 1; pre >= 0; pre--)
                //如果当前选择的数subset和之前的状态pre有相同的质数选择,那么就不能转移
                if((pre & subset) != 0)
                    continue;
                //子集数目等于原来的子集数目乘上i的数目
                f[pre | subset] = (f[pre | subset] + f[pre] * count[i]) % MOD;
            
        
        //统计所有不为0的状态
        long res = 0;
        for(int i = 1; i < mask; i++)
            res = (res + f[i]) % MOD;
        
        //考虑1的情况,有几个1翻几倍
        for(int i = 0; i < count[1]; i++)
            res = res * 2 % MOD;
        
        return (int)res;
    

以上是关于LeetCode 717. 1 比特与 2 比特字符 / 838. 推多米诺 / 1994. 好子集的数目(状态压缩动态规划)的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 717 1比特与2比特字符[循环] HERODING的LeetCode之路

LeetCode 717. 1 比特与 2 比特字符 / 838. 推多米诺 / 1994. 好子集的数目(状态压缩动态规划)

c++刷LeetCode

c++刷LeetCode

717.1比特与2比特字符

「 每日一练,快乐水题 」717. 1比特与2比特字符