LeetCode 371. 两整数之和(异或操作) / 639. 解码方法 II(动态规划)/ 437. 路径总和 III

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 371. 两整数之和(异或操作) / 639. 解码方法 II(动态规划)/ 437. 路径总和 III相关的知识,希望对你有一定的参考价值。

371. 两整数之和

2021.9.26 每日一题

题目描述

给你两个整数 a 和 b ,不使用 运算符 + 和 - ​​​​​​​,计算并返回两整数之和。

示例 1:

输入:a = 1, b = 2
输出:3

示例 2:

输入:a = 2, b = 3
输出:5

提示:

-1000 <= a, b <= 1000

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

思路

从低位到高位进行计算,然后记录进位add,因为有负数,所以要32位全部进行计算
因为负数是补码形式,所以直接加也没问题

class Solution {
    public int getSum(int a, int b) {
        //用二进制算
        int sum = 0;
        int add = 0;
        for(int i = 0; i < 32; i++){
            int x = a & 1;
            int y = b & 1;
            int temp = x ^ y ^ add;
            add = ((x & y) | (x & add) | (y & add));
            a >>= 1;
            b >>= 1;
            sum = (temp << i) | sum;
        }
        return sum;
    }
}

然后发现异或能得到当前位,相与能得到进位,
所以先异或得到当前位,然后相与得到进位add
如果add等于0了,就说明计算停止了

class Solution {
    public int getSum(int a, int b) {
        //用二进制算
        int sum = 0;    //当前位
        int add = 0;    //进位
        while(b != 0){
            sum = a ^ b;            //异或得到当前位
            add = (a & b) << 1;      //然后相与得到进位
            b = add;
            a = sum;
        }
        return a;
    }
}

639. 解码方法 II

2021.9.27 每日一题

题目描述

一条包含字母 A-Z 的消息通过以下的方式进行了编码:

‘A’ -> 1
‘B’ -> 2

‘Z’ -> 26
要 解码 一条已编码的消息,所有的数字都必须分组,然后按原来的编码方案反向映射回字母(可能存在多种方式)。例如,“11106” 可以映射为:

“AAJF” 对应分组 (1 1 10 6)
“KJF” 对应分组 (11 10 6)
注意,像 (1 11 06) 这样的分组是无效的,因为 “06” 不可以映射为 ‘F’ ,因为 “6” 与 “06” 不同。

除了 上面描述的数字字母映射方案,编码消息中可能包含 '’ 字符,可以表示从 ‘1’ 到 ‘9’ 的任一数字(不包括 ‘0’)。例如,编码字符串 "1" 可以表示 “11”、“12”、“13”、“14”、“15”、“16”、“17”、“18” 或 “19” 中的任意一条消息。对 “1*” 进行解码,相当于解码该字符串可以表示的任何编码消息。

给你一个字符串 s ,由数字和 ‘*’ 字符组成,返回 解码 该字符串的方法 数目 。

由于答案数目可能非常大,返回对 109 + 7 取余 的结果。

示例 1:

输入:s = “"
输出:9
解释:这一条编码消息可以表示 “1”、“2”、“3”、“4”、“5”、“6”、“7”、“8” 或 “9” 中的任意一条。
可以分别解码成字符串 “A”、“B”、“C”、“D”、“E”、“F”、“G”、“H” 和 “I” 。
因此,"
” 总共有 9 种解码方法。

示例 2:

输入:s = “1*”
输出:18
解释:这一条编码消息可以表示 “11”、“12”、“13”、“14”、“15”、“16”、“17”、“18” 或 “19” 中的任意一条。
每种消息都可以由 2 种方法解码(例如,“11” 可以解码成 “AA” 或 “K”)。
因此,“1*” 共有 9 * 2 = 18 种解码方法。

示例 3:

输入:s = “2*”
输出:15
解释:这一条编码消息可以表示 “21”、“22”、“23”、“24”、“25”、“26”、“27”、“28” 或 “29” 中的任意一条。
“21”、“22”、“23”、“24”、“25” 和 “26” 由 2 种解码方法,但 “27”、“28” 和 “29” 仅有 1 种解码方法。
因此,“2*” 共有 (6 * 2) + (3 * 1) = 12 + 3 = 15 种解码方法。

提示:

1 <= s.length <= 10^5
s[i] 是 0 - 9 中的一位数字或字符 ‘*’

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

思路

其实不难,一个很好理解的动态规划,看前一位的字符是什么,然后规划
但是这里要注意数据的范围
只变成long,还是会溢出
只取余,因为有可能乘以15,乘以9这种操作还是会导致溢出
所以要long + 取余,才能保证结果的正确性

class Solution {
    public static final int MOD = (int)1e9 + 7;
    public int numDecodings(String s) {
        //动态规划一看就是
        int l = s.length();
        if(s.charAt(0) == '0')
            return 0;
        long[] dp = new long[l + 1];
        dp[0] = 1;  //初始化,为了处理方便
        dp[1] = s.charAt(0) == '*' ? 9 : 1;
        for(int i = 2; i <= l; i++){
            char c = s.charAt(i - 1);
            char pre = i == 1 ? ' ' : s.charAt(i - 2);
            //首先处理单个字符的情况
            //如果是0,只能和前面组合
            if(c == '0'){
                //如果前面的数不满足条件,说明0无法被匹配,返回0
                if(pre == '0' || (pre > '2' && pre <= '9'))
                    return 0;
                //如果是1,2,只有一种情况
                else if(pre == '1' || pre == '2')
                    dp[i] = dp[i - 2];
                //如果是*
                else
                    dp[i] = dp[i - 2] * 2 % MOD;
            }else if(c == '*'){
                if(pre == '0' || (pre > '2' && pre <= '9'))
                    dp[i] = dp[i - 1] * 9 % MOD;
                else if(pre == '1')
                    dp[i] = (dp[i - 2] * 9 % MOD + dp[i - 1] * 9 % MOD) % MOD;
                else if(pre == '2')
                    dp[i] = (dp[i - 2] * 6 % MOD + dp[i - 1] * 9 % MOD) % MOD;
                //如果前面也是*
                else
                    dp[i] = (dp[i - 1] * 9 % MOD + dp[i - 2] * 15 % MOD) % MOD;
            //剩余情况,c就是一个数字
            }else{
                //首先单个数字组成的情况
                dp[i] = dp[i - 1];
                //其次,看和前面的情况
                if(pre == '1')
                    dp[i] = (dp[i] + dp[i - 2]) % MOD;
                else if(pre == '2' && c <= '6'){
                    dp[i] = (dp[i] + dp[i - 2]) % MOD;
                }else if(pre == '*'){
                    dp[i] = (dp[i] + dp[i - 2]) % MOD;
                    if(c <= '6')
                        dp[i] = (dp[i] + dp[i - 2]) % MOD;
                }
            }
        }
        return (int)dp[l];
    }
}

学习三叶姐的第二个动规,不是按照两个字符分情况讨论的
而是遍历26个字符,然后判断两个字符是否能组成这些字母来处理的
代码更简洁
但是这里用了一个f[3]来表示三个变量,可能让人有点迷糊,其实用三个变量来表示就更简单易懂了

class Solution {
    int mod = (int)1e9+7;
    public int numDecodings(String s) {
        int n = s.length();
        long[] f = new long[3];
        f[0] = 1;
        for (int i = 1; i <= n; i++) {
            char c = s.charAt(i - 1);
            int t = c - '0';
            long cnt = 0;
            int p1 = (i - 1) % 3, p2 = (i - 2) % 3;
            // 枚举组成什么 item(A -> 1; B -> 2 ...)
            for (int item = 1; item <= 26; item++) { 
                if (item < 10) { // 该 item 由一个字符组成
                    if (c == '*' || t == item) cnt += f[p1];
                } else { // 该 item 由两个字符组成
                    if (i - 2 < 0) break;
                    char prev = s.charAt(i - 2);
                    int u = prev - '0';
                    int a = item / 10, b = item % 10;
                    if ((prev == '*' || u == a) && (t == b || (c == '*' && b != 0))) cnt += f[p2];
                }
            }
            f[i % 3] = cnt % mod;
        }
        return (int)(f[n % 3]);
    }
}

作者:AC_OIer
链接:https://leetcode-cn.com/problems/decode-ways-ii/solution/gong-shui-san-xie-fen-qing-kuang-tao-lun-902h/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这种打表的应该更快,爱了

class Solution {
    int MOD = (int)1e9 + 7;
    static HashMap<String, Integer> one = new HashMap<>(){}, two = new HashMap<>();
    static{
        for(int i=1;i<10;i++)
            one.put(String.format("%d", i), 1);
        one.put("*", 9);
        for(int i=10;i<27;i++)
            two.put(String.format("%d", i), 1);
        for(int i=0;i<7;i++)
            two.put("*"+i, 2);
        for(int i=7;i<10;i++)
            two.put("*"+i, 1);
        two.put("1*", 9);
        two.put("2*", 6);
        two.put("**", 15);
    }
    public int numDecodings(String s) {
        int dp0 = 1, dp1 = one.getOrDefault(s.substring(0,1),0);
        for(int i=1,tmp=0;i<s.length();i++){
            tmp = dp0;
            dp0 = dp1;
            dp1 = (int)(((long)dp1 * one.getOrDefault(s.substring(i,i+1),0) % MOD + (long)tmp * two.getOrDefault(s.substring(i-1,i+1), 0) % MOD) % MOD);
        }
        return dp1;
    }
}

作者:himymBen
链接:https://leetcode-cn.com/problems/decode-ways-ii/solution/pythonjava-dong-tai-gui-hua-by-himymben-9ulz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

437. 路径总和 III

2021.9.28 每日一题

题目描述

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

示例 1:

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。

示例 2:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:3

提示:

二叉树的节点个数的范围是 [0,1000]
-10^9 <= Node.val <= 10^9
-1000 <= targetSum <= 1000

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

思路

一个简单的回溯,记录每个节点处的和,然后回溯的时候将这个和从集合中去掉
因为和可能出现多次,所以不能用set
再考虑为了方便查找,所以用map

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    Map<Integer, Integer> map = new HashMap<>();
    int count = 0;
    int targetSum;
    public int pathSum(TreeNode root, int targetSum) {
        //我想到一个思路就是创建每条路径的前缀和
        //然后每次遍历创建一个集合,然后计算以这个点为结尾的,是否有何为target的路径
        //然后突然想到,每次遍历到一个位置,然后因为target是确定的,可以直接在set集合中查到,所以应该遍历一次就完事了
        //set不行,因为如果有重复的不会记录,所以得map,记录次数
        this.targetSum = targetSum;
        map.put(0, 1);
        dfs(root, 0);
        return count;
    }

    public void dfs(TreeNode node, int sum){
        if(node == null)
            return;
        sum += node.val;
        if(map.containsKey(sum - targetSum) && map.get(sum - targetSum) > 0){
            count += map.get(sum - targetSum);
        }
        map.put(sum, map.getOrDefault(sum, 0) + 1);
        dfs(node.left, sum);
        dfs(node.right, sum);
        map.put(sum, map.get(sum) - 1);
    }

}

以上是关于LeetCode 371. 两整数之和(异或操作) / 639. 解码方法 II(动态规划)/ 437. 路径总和 III的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode 371. 两整数之和

LeetCode 371 两整数之和[位运算] HERODING的LeetCode之路

LeetCode 371. 两整数之和 Sum of Two Integers

LeetCode 371. 两整数之和 Sum of Two Integers

LeetCode刷题简单-371-两整数之和(一会三过)

LeetCode刷题简单-371-两整数之和(一会三过)