leetcode 题解 673. 最长递增子序列的个数

160 阅读2分钟

image.png

方法:动态规划

思路:

  • 1.先根据动态规划的方程计算出最长的序列
    • 1.1 先找是什么类型的动态规划:易发现是属于单串类型,属于依赖前方比i小的O(n)的子问题,可得到状态转移方程:f(i) = Max(f(i), f(i-1)+1, f(i-2)+1, ...)
    • 1.2 根据状态转移方程写代码,可以得到最长的子序列的长度
  • 2.存储每个位置(i)上的最长子序列由前方(i-1、i-2、...)的子序列组成的个数
  • 3.根据一些特殊情况做处理

代码(JavaScript)

function findNumberOfLIS(nums) {
  // 动态规划常用数组名称,
  // 用于装载对应位置的递增子序列的长度
  const dp = new Array(nums.length).fill(1);
  // 用于记录对应位置的长度中,拥有多少种不同的序列
  const count = new Array(nums.length).fill(1);

  for(let i = 0; i < nums.length; i++) {
    for(let j = 0; j < i; j++) {
      // 通过判断i位置与j位置的大小
      if(nums[j] < nums[i]) { // 如果i位置要比j位置大则会更新dp[i]的内容
        if(dp[j] + 1 > dp[i]) { // 这里表示第一次发现dp[j]为最大,因此直接赋值
          // 存在以下两种情况:
          // 1、可能这个dp[j]还不是最大
          count[i] = count[j];
        } else if(dp[j] + 1 == dp[i]) {
          // 2、可能这个dp[j]之后还有和它一样大的值,此时需要进行数量的叠加
          count[i] += count[j];
        }
        // 更新dp[i]的长度
        dp[i] = Math.max(dp[j] + 1, dp[i]);
      }
    }
  }
  // 取到最大的值对应的长度、以及在dp所在的索引位置index
  const max = Math.max(...dp), index = dp.indexOf(max);
  let result = 0, finalResult = 0;
  for(let i = 0; i < dp.length; i++) {
    if(dp[i] == max) {
      // 1.统计最大长度的数量
      result++;
      // 2.叠加最大长度的子序列个数
      finalResult += count[i];
    }
  }
  // 如果最大长度不唯一,则返回对应叠加的值
  if(result != 1) return finalResult;
  // 否则直接返回最大值对应的数量就可以
  return count[index];
};
let nums = [1, 3, 5, 4, 7];
console.log(findNumberOfLIS(nums));
nums = [2, 2, 2, 2, 2];
console.log(findNumberOfLIS(nums));
nums = [1,2,4,3,5,4,7,2];
console.log(findNumberOfLIS(nums));
nums = [1,2];
console.log(findNumberOfLIS(nums));
nums = [1,3,2];
console.log(findNumberOfLIS(nums));
nums = [1,3,2, 4, 3, 4];
console.log(findNumberOfLIS(nums));
nums = [1,1,1,2,2,2,3,3,3];
console.log(findNumberOfLIS(nums));