【LeetCode】Sama的个人记录_23

 

 

【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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值