LeetCode 之 子序列問題

f6761c452567e3dcd2b9a8f37811f5f7.jpg

674. 最長連續(xù)遞增序列

給定一個未經(jīng)排序的整數(shù)數(shù)組,找到最長且 連續(xù)遞增的子序列,并返回該序列的長度。

連續(xù)遞增的子序列 可以由兩個下標(biāo) l 和 r(l < r)確定,如果對于每個 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是連續(xù)遞增子序列。

示例 1:

輸入:nums = [1,3,5,4,7]
輸出:3
解釋:最長連續(xù)遞增序列是 [1,3,5], 長度為3。
盡管 [1,3,5,7] 也是升序的子序列, 但它不是連續(xù)的,因?yàn)?5 和 7 在原數(shù)組里被 4 隔開。

示例 2:

輸入:nums = [2,2,2,2,2]
輸出:1
解釋:最長連續(xù)遞增序列是 [2], 長度為1。

1. 確定 dp 以及下標(biāo)的含義

dp[i]以下標(biāo)i為結(jié)尾的數(shù)組的連續(xù)遞增的子序列長度為 dp[i]。

2. 確定遞推公式

如果 nums[i + 1] > nums[i],那么以 i+1 為結(jié)尾的數(shù)組的連續(xù)遞增的子序列長度一定等于以i為結(jié)尾的數(shù)組的連續(xù)遞增的子序列長度 + 1 。

即:dp[i + 1] = dp[i] + 1

3. dp 數(shù)組初始化

以下標(biāo)i為結(jié)尾的數(shù)組的連續(xù)遞增的子序列長度最少也應(yīng)該是1,即就是nums[i]這一個元素。所以dp[i]應(yīng)該初始1。

4. 確定遍歷順序

從遞推公式上可以看出, dp[i + 1] 依賴 dp[i],所以一定是從前向后遍歷。

for (let i = 1; i < nums.length; i++) {
  if (nums[i - 1] < nums[i]) {
    dp[i] = dp[i - 1] + 1
  }
}

完整代碼

let findLengthOfLCIS = function (nums) {
  if (nums.length === 1) return 1
  let dp = new Array(nums.length)
  let max = 0
  dp[0] = 1

  for (let i = 1; i < nums.length; i++) {
    if (nums[i - 1] < nums[i]) {
      dp[i] = dp[i - 1] + 1
    } else {
      dp[i] = 1
    }
    max = Math.max(max, dp[i])
  }
  return max
}

300. 最長遞增子序列

給你一個整數(shù)數(shù)組 nums ,找到其中最長嚴(yán)格遞增子序列的長度。

子序列是由數(shù)組派生而來的序列,刪除(或不刪除)數(shù)組中的元素而不改變其余元素的順序。例如,[3,6,2,7] 是數(shù)組 [0,3,1,6,2,2,7] 的子序列。

示例 1:

輸入:nums = [10,9,2,5,3,7,101,18]
輸出:4
解釋:最長遞增子序列是 [2,3,7,101],因此長度為 4 。

示例 2:

輸入:nums = [0,1,0,3,2,3]
輸出:4

示例 3:

輸入:nums = [7,7,7,7,7,7,7]
輸出:1

1. 確定 dp 以及下標(biāo)的含義

dp[i] 表示i之前包括i的最長上升子序列。

2. 狀態(tài)轉(zhuǎn)移方程

位置 i 的最長升序子序列等于 j0i-1 各個位置的最長升序子序列 + 1 的最大值。

所以:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);

這里不是要 dp[i]dp[j] + 1進(jìn)行比較,而是要取dp[j] + 1的最大值。

3. dp[i] 的初始化

每一個i,對應(yīng)的 dp[i](即最長上升子序列)起始大小至少都是是1。

4. 確定遍歷順序

dp[i] 是有0到i-1各個位置的最長升序子序列推導(dǎo)而來,那么遍歷i一定是從前向后遍歷。

j其實(shí)就是0到i-1,遍歷i的循環(huán)里外層,遍歷j則在內(nèi)層,代碼如下:

for (let i = 1; i < nums.length; i++) {
  for (let j = 0; j < i; j++) {
    if (nums[i] > nums[j]) {
      dp[i] = Math.max(dp[i], dp[j] + 1)
    }
  }
}

完整代碼

var lengthOfLIS = function (nums) {
  if (nums.length == 0) {
    return 0
  }
  let dp = new Array(nums.length)
  dp[0] = 1
  let maxans = 1

  for (let i = 1; i < nums.length; i++) {
    dp[i] = 1
    for (let j = 0; j < i; j++) {
      if (nums[i] > nums[j]) {
        dp[i] = Math.max(dp[i], dp[j] + 1)
      }
    }
    maxans = Math.max(maxans, dp[i])
  }
  return maxans
};

673. 最長遞增子序列的個數(shù)

給定一個未排序的整數(shù)數(shù)組,找到最長遞增子序列的個數(shù)。

示例 1:

輸入: [1,3,5,4,7]
輸出: 2
解釋: 有兩個最長遞增子序列,分別是 [1, 3, 4, 7] 和[1, 3, 5, 7]。

示例 2:

輸入: [2,2,2,2,2]
輸出: 5
解釋: 最長遞增子序列的長度是1,并且存在5個子序列的長度為1,因此輸出5。

完整代碼

var findNumberOfLIS = function (nums) {
  let n = nums.length, maxLen = 0, ans = 0;
  const dp = new Array(n).fill(0);
  const cnt = new Array(n).fill(0);
  for (let i = 0; i < n; ++i) {
    dp[i] = 1;
    cnt[i] = 1;
    for (let j = 0; j < i; ++j) {
      if (nums[i] > nums[j]) {
        if (dp[j] + 1 > dp[i]) {
          dp[i] = dp[j] + 1;
          cnt[i] = cnt[j]; // 重置計數(shù)
        } else if (dp[j] + 1 === dp[i]) {
          cnt[i] += cnt[j];
        }
      }
    }
    if (dp[i] > maxLen) {
      maxLen = dp[i];
      ans = cnt[i]; // 重置計數(shù)
    } else if (dp[i] === maxLen) {
      ans += cnt[i];
    }
  }
  return ans;
};

1027. 最長等差數(shù)列

給定一個整數(shù)數(shù)組 A,返回 A 中最長等差子序列的長度。

回想一下,A 的子序列是列表 A[i_1], A[i_2], ..., A[i_k] 其中 0 <= i_1 < i_2 < ... < i_k <= A.length - 1。并且如果 B[i+1] - B[i]( 0 <= i < B.length - 1) 的值都相同,那么序列 B 是等差的。

示例 1:

輸入:[3,6,9,12]
輸出:4
解釋:
整個數(shù)組是公差為 3 的等差數(shù)列。

示例 2:

輸入:[9,4,7,2,10]
輸出:3
解釋:
最長的等差子序列是 [4,7,10]。

示例 3:

輸入:[20,1,15,3,10,5,8]
輸出:4
解釋:
最長的等差子序列是 [20,15,10,5]。

var longestArithSeqLength = function(nums) {
    const len = nums.length
    const dp = Array.from({ length: len }, () => new Array(len).fill(2))
    let ans = 2
    for (let i = 1; i < len; i++) { // 中間的數(shù)字
      for (let j = i + 1; j < len; j++) { // 后面的數(shù)字
         for (let k = 0; k < i; k++) { // 前面的數(shù)字
             if (nums[j] - nums[i] === nums[i] - nums[k]) { // 構(gòu)成等差數(shù)列
                dp[i][j] = Math.max(dp[k][i] + 1, dp[i][j])
                ans = Math.max(dp[i][j], ans)
              }
            }
          }
        }
    return ans
};

334. 遞增的三元子序列

給你一個整數(shù)數(shù)組 nums ,判斷這個數(shù)組中是否存在長度為 3 的遞增子序列。

如果存在這樣的三元組下標(biāo) (i, j, k) 且滿足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否則,返回 false 。

示例 1:

輸入:nums = [1,2,3,4,5]
輸出:true
解釋:任何 i < j < k 的三元組都滿足題意

示例 2:

輸入:nums = [5,4,3,2,1]
輸出:false
解釋:不存在滿足題意的三元組

示例 3:

輸入:nums = [2,1,5,0,4,6]
輸出:true
解釋:三元組 (3, 4, 5) 滿足題意,因?yàn)?nums[3] == 0 < nums[4] == 4 < nums[5] == 6

這道題是 300. 最長遞增子序列 變種過來的,思路差不多,只有處理一些小細(xì)節(jié)就可以了。當(dāng)然肯定有更加優(yōu)秀的解法,這里就留給讀者發(fā)揮了。

1. 確定 dp 以及下標(biāo)的含義

dp[i] 表示i之前包括i的最長上升子序列。

2. 狀態(tài)轉(zhuǎn)移方程

位置 i 的最長升序子序列等于 j0i-1 各個位置的最長升序子序列 + 1 的最大值。

所以:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);

這里不是要 dp[i]dp[j] + 1進(jìn)行比較,而是要取dp[j] + 1的最大值。

3. dp[i] 的初始化

每一個i,對應(yīng)的 dp[i](即最長上升子序列)起始大小至少都是是1。

4. 確定遍歷順序

dp[i] 是有0到i-1各個位置的最長升序子序列推導(dǎo)而來,那么遍歷i一定是從前向后遍歷。

j其實(shí)就是0到i-1,遍歷i的循環(huán)里外層,遍歷j則在內(nèi)層,代碼如下:

for (let i = 1; i < nums.length; i++) {
  for (let j = 0; j < i; j++) {
    if (nums[i] > nums[j]) {
      dp[i] = Math.max(dp[i], dp[j] + 1)
    }
  }
}

完整代碼

var increasingTriplet = function (nums) {
  if (nums.length == 0) {
    return 0
  }
  if (Array.from(new Set(nums)).length === 2) {
      return false
  }
  let dp = new Array(nums.length)
  dp[0] = 1
  let maxans = 1

  for (let i = 1; i < nums.length; i++) {
    dp[i] = 1
    for (let j = 0; j < i; j++) {
      if (nums[i] > nums[j]) {
        dp[i] = Math.max(dp[i], dp[j] + 1)
      }
    }
    maxans = Math.max(maxans, dp[i])
    if (maxans >= 3) {
        return true
    }
  }
  return false
};

646. 最長數(shù)對鏈

給出 n 個數(shù)對。 在每一個數(shù)對中,第一個數(shù)字總是比第二個數(shù)字小。

現(xiàn)在,我們定義一種跟隨關(guān)系,當(dāng)且僅當(dāng) b < c 時,數(shù)對(c, d) 才可以跟在 (a, b) 后面。我們用這種形式來構(gòu)造一個數(shù)對鏈。

給定一個數(shù)對集合,找出能夠形成的最長數(shù)對鏈的長度。你不需要用到所有的數(shù)對,你可以以任何順序選擇其中的一些數(shù)對來構(gòu)造。

示例:

輸入:[[1,2], [2,3], [3,4]]
輸出:2
解釋:最長的數(shù)對鏈?zhǔn)?[1,2] -> [3,4]

這道題是 300. 最長遞增子序列 變種過來的,思路差不多,只有處理一些小細(xì)節(jié)就可以了。當(dāng)然肯定有更加優(yōu)秀的解法,這里就留給讀者發(fā)揮了。

先對 envelopes 進(jìn)行排序,之后進(jìn)行套模板就行了。

1. 確定 dp 以及下標(biāo)的含義

dp[i] 表示i之前包括i的最長上升子序列。

2. 狀態(tài)轉(zhuǎn)移方程

位置 i 的最長升序子序列等于 j0i-1 各個位置的最長升序子序列 + 1 的最大值。

所以:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);

這里不是要 dp[i]dp[j] + 1進(jìn)行比較,而是要取dp[j] + 1的最大值。

3. dp[i] 的初始化

每一個i,對應(yīng)的 dp[i](即最長上升子序列)起始大小至少都是是1。

4. 確定遍歷順序

dp[i] 是有0到i-1各個位置的最長升序子序列推導(dǎo)而來,那么遍歷i一定是從前向后遍歷。

j其實(shí)就是0到i-1,遍歷i的循環(huán)里外層,遍歷j則在內(nèi)層,代碼如下:

for (let i = 1; i < nums.length; i++) {
  for (let j = 0; j < i; j++) {
    if (envelopes[i][0] > envelopes[j][1]) {
      dp[i] = Math.max(dp[i], dp[j] + 1)
    }
  }
}

完整代碼

var findLongestChain = function (envelopes) {
  let n = envelopes.length;
  if (n < 1) return 0;
  let max = 1;
  let dp = new Array(n).fill(1);
  dp[0] = 1;
  envelopes.sort((a, b) => a[0] - b[0])

  for (let i = 1; i < n; i++) {
    for (let j = 0; j < i; j++) {
      if (envelopes[i][0] > envelopes[j][1]) {
        dp[i] = Math.max(dp[i], dp[j] + 1)
      }
    }
    max = Math.max(max, dp[i]);
  }

  return max;
};

354. 俄羅斯套娃信封問題

給你一個二維整數(shù)數(shù)組 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 個信封的寬度和高度。

當(dāng)另一個信封的寬度和高度都比這個信封大的時候,這個信封就可以放進(jìn)另一個信封里,如同俄羅斯套娃一樣。

請計算 最多能有多少個 信封能組成一組“俄羅斯套娃”信封(即可以把一個信封放到另一個信封里面)。

注意:不允許旋轉(zhuǎn)信封。

示例 1:

輸入:envelopes = [[5,4],[6,4],[6,7],[2,3]]
輸出:3
解釋:最多信封的個數(shù)為 3, 組合為: [2,3] => [5,4] => [6,7]。

示例 2:

輸入:envelopes = [[1,1],[1,1],[1,1]]
輸出:1

這道題是 300. 最長遞增子序列 變種過來的,思路差不多,只有處理一些小細(xì)節(jié)就可以了。當(dāng)然肯定有更加優(yōu)秀的解法,這里就留給讀者發(fā)揮了。

先對 envelopes 進(jìn)行排序,之后進(jìn)行套模板就行了。

1. 確定 dp 以及下標(biāo)的含義

dp[i] 表示i之前包括i的最長上升子序列。

2. 狀態(tài)轉(zhuǎn)移方程

位置 i 的最長升序子序列等于 j0i-1 各個位置的最長升序子序列 + 1 的最大值。

所以:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);

這里不是要 dp[i]dp[j] + 1進(jìn)行比較,而是要取dp[j] + 1的最大值。

3. dp[i] 的初始化

每一個i,對應(yīng)的 dp[i](即最長上升子序列)起始大小至少都是是1。

4. 確定遍歷順序

dp[i] 是有0到i-1各個位置的最長升序子序列推導(dǎo)而來,那么遍歷i一定是從前向后遍歷。

j其實(shí)就是0到i-1,遍歷i的循環(huán)里外層,遍歷j則在內(nèi)層,代碼如下:

for (let i = 1; i < nums.length; i++) {
  for (let j = 0; j < i; j++) {
    if (envelopes[i][0] > envelopes[j][0] && envelopes[i][1] > envelopes[j][1]) {
      dp[i] = Math.max(dp[i], dp[j] + 1)
    }
  }
}

完整代碼

var maxEnvelopes = function (envelopes) {
  let n = envelopes.length;
  if (n < 1) return 0;
  let max = 1;
  let dp = new Array(n).fill(1);
  dp[0] = 1;
  envelopes.sort((a, b) => a[0] - b[0])

  for (let i = 1; i < n; i++) {
    for (let j = 0; j < i; j++) {
      if (envelopes[i][0] > envelopes[j][0] && envelopes[i][1] > envelopes[j][1]) {
        dp[i] = Math.max(dp[i], dp[j] + 1)
      }
    }
    max = Math.max(max, dp[i]);
  }

  return max;
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容