求数组最长严格递增子序列的长度。
D [ i ] D[i] D[i]代表以 n u m s [ i ] nums[i] nums[i]结尾的最长递增子序列的长度。
D [ i ] = max j < i , n u m s [ i ] > n u m s [ j ] ( D [ j ] + 1 ) D[i] = \max_{j < i,\ nums[i]>nums[j]}(D[j] + 1) D[i]=j<i, nums[i]>nums[j]max(D[j]+1)
Q1: O ( n 2 ) O(n^2) O(n2)版本如何提速到 O ( n log n ) O(n\log n) O(nlogn)?
Q2:能否输出路径?这个不难,增加一个 r e c o r d record record数组就好。
写了第二遍了,但完全找不到贪心+二分查找的记忆……但看了一遍又不得不感叹巧妙。
贪心思想:保持长度为 i i i的递增子序列,尾部元素值最小。比如[1,2,3,5]
和[1,2,3,4]
,优先选择 [ 1 , 2 , 3 , 4 ] [1,2,3,4] [1,2,3,4]。
维护一个列表 t a i l tail tail, t a i l [ i ] tail[i] tail[i]表示长度为 i + 1 i+1 i+1的递增子序列,能够获得的最小尾部元素值。
注意: t a i l tail tail数组中的元素并不一定是路径,反例为下图中蓝色部分。
D [ i ] = n u m s [ i ] > n u m s [ i − 1 ] ? D [ i − 1 ] + 1 : 0 D[i] = nums[i]>nums[i-1]\ ?\ D[i-1] + 1 : 0 D[i]=nums[i]>nums[i−1] ? D[i−1]+1:0
class Solution {
public int findLengthOfLCIS(int[] nums) {
int maxLength = 1;
int length = 1;
for (int i = 1; i < nums.length; i++) {
length = nums[i] > nums[i - 1] ? length + 1 : 1;
maxLength = Math.max(length, maxLength);
}
return maxLength;
}
}
给定两个数组,返回两个数组中公共的、长度最长的子数组。
令 D [ i , j ] D[i,j] D[i,j]表示以 n u m s 1 [ i ] 、 n u m s 2 [ j ] nums1[i]、nums2[j] nums1[i]、nums2[j]结尾的公共子数组最大长度。
TODO:滑动窗口解法。
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int[][] D = new int[m][n];
int max = Integer.MIN_VALUE;
for (int i = 0; i < m; i++) {
D[i][0] = nums1[i] == nums2[0] ? 1 : 0;
max = Math.max(D[i][0], max);
}
for (int j = 0; j < n; j++) {
D[0][j] = nums1[0] == nums2[j] ? 1 : 0;
max = Math.max(D[0][j], max);
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
D[i][j] = nums1[i] == nums2[j] ? D[i - 1][j - 1] + 1 : 0;
max = Math.max(D[i][j], max);
}
}
return max;
}
}
令 D [ i , j ] D[i,j] D[i,j]表示 n u m s 1 [ 0 : i ] 、 n u m s [ 0 : j ] nums1[0:i]、nums[0:j] nums1[0:i]、nums[0:j]的公共子序列最大长度。
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
char[] str1 = text1.toCharArray();
char[] str2 = text2.toCharArray();
int m = str1.length;
int n = str2.length;
int[][] D = new int[m][n];
D[0][0] = str1[0] == str2[0] ? 1 : 0;
for (int i = 1; i < m; i++) {
D[i][0] = D[i - 1][0] == 1 || str1[i] == str2[0] ? 1 : 0;
}
for (int j = 1; j < n; j++) {
D[0][j] = D[0][j - 1] == 1 || str1[0] == str2[j] ? 1 : 0;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
D[i][j] = str1[i] == str2[j] ? D[i - 1][j - 1] + 1 : Math.max(D[i - 1][j], D[i][j - 1]);
}
}
return D[m - 1][n -1];
}
}
感觉和上一题是一样滴耶。
令 D [ i , j ] D[i,j] D[i,j]表示 n u m s 1 [ 0 : i ] 、 n u m s [ 0 : j ] nums1[0:i]、nums[0:j] nums1[0:i]、nums[0:j]最多不相交的线的数量。
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int[][] D = new int[m][n];
D[0][0] = nums1[0] == nums2[0] ? 1 : 0;
for (int i = 1; i < m; i++) {
D[i][0] = D[i - 1][0] == 1 || nums1[i] == nums2[0] ? 1 : 0;
}
for (int j = 1; j < n; j++) {
D[0][j] = D[0][j - 1] == 1 || nums1[0] == nums2[j] ? 1 : 0;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
D[i][j] = nums1[i] == nums2[j] ? D[i-1][j-1] + 1 : Math.max(D[i][j - 1], D[i - 1][j]);
}
}
return D[m - 1][n - 1];
}
}
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
相当于只使用删除操作的编辑距离问题。
和之前问题的不同:只对其中一个串进行操作,利用另一个串的信息(而不进行操作)。
令 D [ i , j ] D[i,j] D[i,j]表示字符串 s [ 0 : i ] s[0:i] s[0:i]和字符串 t [ 0 : j ] t[0:j] t[0:j]的以 s [ i ] s[i] s[i]结尾的最大公共子序列长度。
D [ i , j ] = { D [ i − 1 ] [ j − 1 ] + 1 , if s [ i ] = s [ j ] D [ i ] [ j − 1 ] , else D[i,j] = \begin{cases} D[i-1][j-1] + 1, & \text{if }s[i]=s[j] \\ D[i][j - 1], & \text{else } \end{cases} D[i,j]={D[i−1][j−1]+1,D[i][j−1],if s[i]=s[j]else
class Solution {
public boolean isSubsequence(String s, String t) {
if (s.length() == 0) {
return true;
}
if (t.length() == 0) {
return false;
}
char[] src = t.toCharArray();
char[] target = s.toCharArray();
int m = target.length;
int n = src.length;
// 初始化
int[][] D = new int[m][n];
D[0][0] = src[0] == target[0] ? 1 : 0;
for (int i = 1; i < m; i++) {
D[i][0] = target[i] == src[0] ? 1 : 0;
}
for (int j = 1; j < n; j++) {
D[0][j] = D[0][j - 1] == 1 || target[0] == src[j] ? 1 : 0;
}
// 计算
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
D[i][j] = target[i] == src[j] ? D[i - 1][j - 1] + 1 : D[i][j - 1];
}
}
return D[m - 1][n - 1] == target.length;
}
}
令 D [ i , j ] D[i,j] D[i,j]表示字符串 s [ 0 : i ] s[0:i] s[0:i]含有子序列 t [ 0 : j ] t[0:j] t[0:j]的个数。
D [ i , j ] = { D [ i − 1 ] [ j − 1 ] + D [ i − 1 ] [ j ] , if s [ i ] = s [ j ] D [ i − 1 ] [ j ] , else D[i,j] = \begin{cases} D[i-1][j-1] + D[i - 1][j], & \text{if }s[i]=s[j] \\ D[i - 1][j], & \text{else } \end{cases} D[i,j]={D[i−1][j−1]+D[i−1][j],D[i−1][j],if s[i]=s[j]else
class Solution {
public int numDistinct(String s, String t) {
char[] src = s.toCharArray();
char[] target = t.toCharArray();
int m = src.length;
int n = target.length;
int[][] D = new int[m][n];
D[0][0] = src[0] == target[0] ? 1 : 0;
for (int i = 1; i < m; i++) {
D[i][0] = D[i - 1][0] + (src[i] == target[0] ? 1 : 0);
}
// 其实这一段不用初始化的,默认就是0,但还是写一下明确一下
for (int j = 1; j < n; j++) {
D[0][j] = 0;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
D[i][j] = src[i] == target[j] ? D[i - 1][j - 1] + D[i - 1][j] : D[i - 1][j];
}
}
return D[m - 1][n - 1];
}
}
D [ i , j ] D[i,j] D[i,j]表示将 w o r d 1 [ 0 : i ] word1[0:i] word1[0:i]和 w o r d 2 [ 0 : j ] word2[0:j] word2[0:j]变为相同串的最小删除步数。
D [ i , j ] = { D [ i − 1 ] [ j − 1 ] , if s [ i ] = s [ j ] min ( D [ i − 1 ] [ j ] , D [ i ] [ j − 1 ] ) + 1 , else D[i,j] = \begin{cases} D[i-1][j-1], & \text{if }s[i]=s[j] \\ \min(D[i - 1][j], D[i][j-1]) + 1, & \text{else } \end{cases} D[i,j]={D[i−1][j−1],min(D[i−1][j],D[i][j−1])+1,if s[i]=s[j]else
class Solution {
public int minDistance(String word1, String word2) {
char[] str1 = word1.toCharArray();
char[] str2 = word2.toCharArray();
int m = str1.length;
int n = str2.length;
int[][] D = new int[m][n];
D[0][0] = str1[0] == str2[0] ? 0 : 2;
// 如果相等,需要删除i个字符,否则删除D[i - 1][0] + 1个字符
for (int i = 1; i < m; i++) {
D[i][0] = str1[i] == str2[0] ? i : D[i - 1][0] + 1;
}
for (int j = 1; j < n; j++) {
D[0][j] = str1[0] == str2[j] ? j : D[0][j - 1] + 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (str1[i] == str2[j]) {
D[i][j] = D[i - 1][j - 1];
}
else {
D[i][j] = Math.min(D[i - 1][j], D[i][j - 1]) + 1;
}
}
}
return D[m - 1][n - 1];
}
}
D [ i , j ] D[i,j] D[i,j]表示将 w o r d 1 [ 0 : i ] word1[0:i] word1[0:i]转换为 w o r d 2 [ 0 : j ] word2[0:j] word2[0:j]的最小操作数。
D [ i , j ] = { D [ i − 1 ] [ j − 1 ] , if s [ i ] = s [ j ] D [ i − 1 ] [ j ] + 1 , else D[i,j] = \begin{cases} D[i-1][j-1], & \text{if }s[i]=s[j] \\ D[i - 1][j]+1, & \text{else } \end{cases} D[i,j]={D[i−1][j−1],D[i−1][j]+1,if s[i]=s[j]else
D [ i , j ] = { D [ i − 1 ] [ j − 1 ] , if s [ i ] = s [ j ] D [ i ] [ j − 1 ] + 1 , else D[i,j] = \begin{cases} D[i-1][j-1], & \text{if }s[i]=s[j] \\ D[i][j - 1]+1, & \text{else } \end{cases} D[i,j]={D[i−1][j−1],D[i][j−1]+1,if s[i]=s[j]else
D [ i , j ] = { D [ i − 1 ] [ j − 1 ] , if s [ i ] = s [ j ] D [ i − 1 ] [ j − 1 ] + 1 , else D[i,j] = \begin{cases} D[i-1][j-1], & \text{if }s[i]=s[j] \\ D[i - 1][j - 1]+1, & \text{else } \end{cases} D[i,j]={D[i−1][j−1],D[i−1][j−1]+1,if s[i]=s[j]else
所以:
D [ i , j ] = { D [ i − 1 ] [ j − 1 ] , if s [ i ] = s [ j ] min ( D [ i − 1 ] [ j ] , D [ i ] [ j − 1 ] , D [ i − 1 ] [ j ] ) + 1 , else D[i,j] = \begin{cases} D[i-1][j-1], & \text{if }s[i]=s[j] \\ \min(D[i-1][j],D[i][j-1],D[i - 1][j])+1, & \text{else } \end{cases} D[i,j]={D[i−1][j−1],min(D[i−1][j],D[i][j−1],D[i−1][j])+1,if s[i]=s[j]else
两种写法,一种从空串开始,更推荐这种写法,不易出错:
class Solution {
public int minDistance(String word1, String word2) {
char[] str1 = word1.toCharArray();
char[] str2 = word2.toCharArray();
int m = str1.length;
int n = str2.length;
int[][] D = new int[m + 1][n + 1];
// 二者都是空串
D[0][0] = 0;
// 把str1[0:i-1]变为空串
for (int i = 1; i <= m; i++) {
D[i][0] = i;
}
// 把空串变为str2[0:j-1]
for (int j = 1; j <= n; j++) {
D[0][j] = j;
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
D[i][j] = str1[i - 1] == str2[j - 1] ? D[i - 1][j - 1] : Math.min(Math.min(D[i - 1][j], D[i][j - 1]), D[i - 1][j - 1]) + 1;
}
}
return D[m][n];
}
}
一种从单个字符开始:
class Solution {
public int minDistance(String word1, String word2) {
if (word1.length() == 0) {
return word2.length();
}
if (word2.length() == 0) {
return word1.length();
}
char[] str1 = word1.toCharArray();
char[] str2 = word2.toCharArray();
int m = str1.length;
int n = str2.length;
int[][] D = new int[m][n];
D[0][0] = str1[0] == str2[0] ? 0 : 1;
for (int i = 1; i < m; i++) {
D[i][0] = str1[i] == str2[0] ? i : D[i - 1][0] + 1;
}
for (int j = 1; j < n; j++) {
D[0][j] = str1[0] == str2[j] ? j : D[0][j - 1] + 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
D[i][j] = str1[i] == str2[j] ? D[i - 1][j - 1] : Math.min(Math.min(D[i - 1][j], D[i][j - 1]), D[i - 1][j - 1]) + 1;
}
}
return D[m - 1][n - 1];
}
}