leetcode上面的题目总是那么经典,经典到到现在笔试过的99%互联网大公司都使用这上面的题。而这其中对于深度优先搜索,广度优先搜索,动归,贪婪这四种较多。本篇文章是关于动归的题目整理,都是在下刷题的心得。在下才疏学浅,没有接触过ACM,只有一年多的刷题史,如有拙言还望见者多多指教。
刷题量不够的人常常遇见编程题不知道使用什么解法,即使知道了可能也会因为没有足够的解题经验和技巧Get不到算法的重点而写不出来。
如果说,对于问题可拆分的类型,拆分后会发现可以把原始问题规模逐渐减小,而减小到一般规模后的解是显而易见的。而且最重要的是不管问题规模的大小,都是属于相同类型的问题。 一般来说有的动归问题可以使用贪婪选择或者深搜解决,但是动归给我自己的感觉主要的特点有两个,一是保存中间状态使用,二是使用自低向上的视角从备忘录(暂且这么叫吧,我记得哪本书上就这么叫)中向原问题构造解。当然,也可以使用自顶向下的递归方式。这一点是动归最迷人的地方,也是难点之处。经典动归问题就是编辑距离问题,而且该算法在输入自动提示中有实际应用。不要把上面的话和深搜广搜弄混,深搜广搜的题目其实有经验的人一眼就能看出来。比如迷宫遍历,八皇后问题,矩阵中找字符串,棋盘搜索,数独等问题都有一种共同的特点就是大规模搜索,使用栈枚举可行解空间,在栈入栈退栈中进行回溯。当然,如果有约束的条件应该在遍历时减少搜索空间。
115. Distinct Subsequences
给定字符串 S 和 T, 统计T在S中唯一的子序列。输出总数量。
子序列不同于子串,子串是连续的。子序列是可以不连续的在字符串中保持原始顺序的序列。题目解释说子序列是在原串中不打乱相对位置同时删除某些字符得来的。
例如"ACE" 是 "ABCDE" 子串, 但是 "AEC" 不是。
测试示例:
S = "rabbbit", T = "rabbit"则T在S中有3种子序列。
public int numDistinct(String s, String t) { int sn = s.length(); int tn = t.length(); int [][] dp = new int[tn + 1][ sn + 1]; for(int i = 0; i < sn + 1; i ++){ dp[0][i] = 1; } for(int i = 1; i < tn + 1; i ++){ for(int j = i; j < sn + 1; j++){ int val = dp[i][j-1]; if (s.charAt(j - 1) == t.charAt(i - 1)){ val += dp[i-1][j-1]; } dp[i][j] = val; } } return dp[tn][sn]; }
思路:这道题其实是编辑距离的简化版,即只允许删除字符一种操作。创建DP记录中间状态时,多创建一行一列方便计算。该题目转移方程为:
设DP[i][j] 为字符串S[0, i-1] 中 T[0, j-1]的子序列数量。
DP[i][j] = 若S[i]等于T[j]处的字符,则可以把该字符算作子序列的一部分。 + 也可以不算入子序列的一部分。 否则, 源串即S后退一位,与T串当前位置继续比较。377. Combination Sum IV
public int combinationSum4(int[] nums, int target) { Arrays.sort(nums); int sum = 0; for(int i = 0; i < nums.length; i++){ sum += nums[i]; } int [] dp = new int [target + 1]; dp[0] = 1; for(int i = 1; i <= target; i++){ int count = 0; for(int j = 0; j < nums.length; j ++){ if(nums[j] <= i)count += dp[i-nums[j]]; } dp[i] = count; } return dp[target]; }
300. Longest Increasing Subsequence
public int lengthOfLIS(int[] nums) { int n = nums.length; if(n == 0)return 0; int [] dp = new int[n]; int Max =1; for(int i = 0; i < n; i++){ int max = 0; for(int j = i-1; j > -1; j--){ if(nums[j] < nums[i])max = Math.max(max, dp[j]); } dp[i] = 1+max; Max = Math.max(Max, dp[i]); } return Max; }
思路:该题解题标签中有DP和贪婪两种方法。在这里很直接使用了贪婪解法。