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 两整数之和[位运算] HERODING的LeetCode之路
LeetCode 371. 两整数之和 Sum of Two Integers