【Q67】(ez) 二进制求和
给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空 字符串且只包含数字 1 和 0。
示例 1: 输入: a = “11”, b = “1” 输出: “100”
示例 2: 输入: a = “1010”, b = “1011” 输出: “10101”
class Solution {
/*
* 模拟还原二进制加法
* 思路不难,但代码不好写,通过一些技巧可以简化很多
*/
public String addBinary(String a, String b) {
StringBuilder sb = new StringBuilder();
int len = Math.max(a.length(), b.length());
int carry = 0; // carry代表【进位】;从最低位开始加。
for(int i = 0; i < len; i++) { // 在两个加数串长度不等的情况下,这两行省去了很多代码
carry += (a.length() - 1 - i >= 0 ? a.charAt(a.length() - 1 - i) - '0' : 0);
carry += (b.length() - 1 - i >= 0 ? b.charAt(b.length() - 1 - i) - '0' : 0);
sb.insert(0, carry % 2); // 本位
carry /= 2; // 进位
}
if(carry > 0) {
sb.insert(0, carry); // 其实此时的carry如果大于零,那么只能是1;如果用sb.append,那么最后还需要reverse反转一下
}
return sb.toString();
}
}
class Solution {
public String addBinary(String a, String b) {
return Integer.toBinaryString(Integer.parseInt(a, 2) + Integer.parseInt(b, 2));
}
/*
* 这种朴素方法的前提是Java或Python本身的高精度运算,其他语言不能使用
*/
}
【Q139】(md) 单词拆分
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。 你可以假设字典中没有重复的单词。
示例 :
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
示例 :
输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false
class Solution {
/*
* 【动态规划】
*
* 这个动态规划的特殊之处在于,不是一次O(n)遍历得到的dp表,而是通过两层for循环的嵌套进行填表
*
* dp[j]的含义是,[前j个字符是否符合条件]
* 动态转移方程:dp[j] = dp[i] && (dict.contains(s.substring(i, j))); 其中i是左指针,j是右指针
*/
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> dict = new HashSet<>(wordDict); // 技巧:转化为HashSet增加效率
int len = s.length();
boolean[] dp = new boolean[len + 1];
dp[0] = true; // 初始化
for(int j = 1; j <= len; j++) {
for(int i = j - 1; i >= 0; i--) { // 技巧:这里的i从大向小取值,效率会更高(更早使得dp[j]为true)
dp[j] = dp[i] && (dict.contains(s.substring(i, j)));
if(dp[j]) break; // 关键
}
}
return dp[len];
}
/*
* 总结:
* 其实这道题也可以使用【回溯(递归)】,但是会超时。
* 尽管dp的复杂度是O(n²),仍明显优于回溯递归
* 功利的讲,回溯法适合【记录路径】。而不适合【是否存在通路】,也就是说当返回值是个集合,那多半使用【回溯】;若返回的是boolean,就往【dp】上想
*/
}
【Q209】(md) 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的连续子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的连续子数组。
class Solution {
/*
* 【滑动窗口】
*
* 模板题。建议背熟。
*/
public int minSubArrayLen(int s, int[] nums) {
int len = nums.length;
if(len == 0) return 0;
int L = 0;
int R = 0;
int sum = 0;
int resLen = Integer.MAX_VALUE;
while(R < len && L <= R ) {
sum += nums[R];
while(sum >= s) {
resLen = Math.min(resLen, R - L + 1);
sum -= nums[L];
L++;
}
R++;
}
return resLen == Integer.MAX_VALUE ? 0 : resLen;
}
// 1.只有一个小技巧:
// 先把resLen设置为一个不可能取到的极大值;当resLen一次都没有被修改过时,就返回0(因为题目这样要求)
// 2.【滑动窗口】是O(n)的线性复杂度
}
class Solution {
/*
* 【前缀和】 + 【二分法】
*
* 这里的【二分】不是指经典的下标二分,也不是【值域二分】,而是【二分查找binarySearch】
* binarySearch的 nlogn 复杂度是可以接受的;再用"前缀和"的思想与这种"直接搜索"的方法相结合,是完全可行的
*/
public int minSubArrayLen(int s, int[] nums) {
int len = nums.length;
if(len == 0) return 0;
int resLen = Integer.MAX_VALUE;
int[] preSum = new int[len + 1]; // 前缀和数组(下标为i处,意义为前i个数的和)
for(int i = 0; i < len; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
for(int L = 0; L < len; L++) {
int R = Arrays.binarySearch(preSum, preSum[L] + s);
if(R == (-len - 2)) continue; // 如果位置在最后面的后面,说明这个和不足以达到目标值
if(R < 0) { // 修正下标位置
R = (-R - 1);
}
resLen = Math.min(resLen, R - L);
}
return resLen == Integer.MAX_VALUE ? 0 : resLen;
}
/*
* 涉及到二分,应该立马想到O(nlogn)的时间复杂度
*/
}
Qs from https://leetcode-cn.com
♣ loli suki
♦ end