手写js: 最长xx(子序列|数组|回文子串...)?

31 阅读5分钟

各种最长?老是混淆?

  • 最长重复子数组
  • 最长公共子序列
  • 最长递增子序列
  • 最长连续递增子序列
  • 最长递增子序列的个数
  • 最长回文子串
  • 最长回文子序列
  • 最长有效括号
  • 最长公共前缀
  • 无重复字符的最长子串

眼花缭乱,凌乱的很

最长公共系列

最长重复子数组

/**
输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。
 */

function findPublicChild(a, b) {
  let [m, n] = [a.length, b.length];
  let dp = new Array(m + 1).fill().map(() => new Array(n + 1).fill(0));
  let max = 0;
  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (a[i - 1] == b[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
      }
      max = Math.max(max, dp[i][j]);
    }
  }
  return max;
}

拓展,求输出公共子数组?

function findPublicChild(a, b) {
  let [m, n] = [a.length, b.length];
  let dp = new Array(m + 1).fill().map(() => new Array(n + 1).fill(0));
  let max = 0;
  let end = 0;
  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (a[i - 1] == b[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
      }
      if (dp[i][j] > max) {
        max = dp[i][j];
        end = i;
      }
    }
  }
  console.log(max, end);
  return a.slice(end - max, end);
}
findPublicChild([5, 7, 8, 10, 11, 12, 34], [3, 2, 7, 8, 10]);

最长公共子序列

/**
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
 */
function findLongestPublicChild(a, b) {
  let [m, n] = [a.length, b.length];
  let dp = new Array(m + 1).fill().map(() => new Array(n + 1).fill(0));
  for (let i = 1; i <= m; i++) {
    for (let j = 1; i <= n; j++) {
      if (a[i - 1] == b[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
      } else {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
      }
      max = Math.max(max, dp[i][j]);
    }
  }
  return max;
}

两个区别是 重复子数组的个数=前一个状态+1,而最长公共子序列分为两种情况,a[i - 1] VS b[j - 1] 相等和不相等。相等的时候,dp[i][j]等于 dp[i-1][j-1]+1.如果不相等,则需要看前面的某一字符串是否相等,即 a[0, i-2]和 b[0, j-1]的最长公共子序列和 a[i-2]和 b[j-1]的最长公共子序列,两者取最大值

if (a[i - 1] == b[j - 1]) {
  dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
  dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}

拓展,求输出公共子序列?

最长递增子序列相关

最长递增子序列

/**
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4
 */
function longestUpChild(nums) {
  let len = nums.length;
  let dp = new Array(len + 1).fill(1);
  let max = 1;
  for (let i = 1; i <= len; i++) {
    for (let j = 0; j < i; j++) {
      if (nums[i] > nums[j]) {
        dp[i] = Math.max(dp[j] + 1, dp[i]);
      }
      max = Math.max(max, dp[i]);
    }
  }
  return max;
}

最长连续递增子序列

/**
输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
 */
function longestUpChildSeq(nums) {
  let dp = new Array(nums.length + 1).fill(1);
  let max = 1;
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] > nums[i - 1]) {
      dp[i] = dp[i - 1] + 1;
    }
    max = Math.max(max, dp[i]);
  }
  return max;
}
function longestUpChildSeq(nums) {
  if (nums.length == 0) return 0;
  let max = 1;
  let temp = [];
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] > nums[i - 1]) {
      temp.push(nums[i]);
    } else {
      temp = [];
    }
    max = Math.max(max, temp.length);
  }
  return max;
}

最长回文相关

最长回文子串

/**
 * 输入:s = "babad"
 * 输出:"bab"
 * 解释:"aba" 同样是符合题意的答案。
 */
function longestPalindrome(s) {
  if (s == '') return s;
  let maxLen = 1;
  let begin = 0;
  let len = s.length;
  let dp = new Array(len).fill().map(() => new Array(len).fill(false));
  for (let i = 0; i < len; i++) {
    dp[i][i] = true;
  }
  for (let i = 1; i < len; i++) {
    for (let j = 0; j < i; j++) {
      if (s[i] == s[j]) {
        if (i - j <= 2) {
          dp[i][j] = true;
        } else {
          dp[i][j] = dp[i - 1][j + 1];
        }
      }
      if (dp[i][j] && i - j + 1 > maxLen) {
        maxLen = i - j + 1;
        begin = j;
      }
    }
  }
  return s.substring(begin, begin + maxLen);
}

还有一种按照代码随想录的解法

function longestPalindrome(s) {
  if (s.length == '') return s;
  let len = s.length;
  let dp = new Array(len).fill().map(() => new Array(len).fill(false));
  let begin = 0;
  let maxLen = 1;
  for (let i = len - 1; i >= 0; i--) {
    for (let j = i; j < len; j++) {
      if (s[i] == s[j]) {
        if (j - i <= 1) {
          dp[i][j] = true;
        } else {
          dp[i][j] = dp[i + 1][j - 1]; // 左下角推出dp[i][j]
        }
      }
      if (dp[i][j] && j - i + 1 > maxLen) {
        maxLen = j - i + 1;
        begin = i;
      }
    }
  }
  return s.substring(begin, begin + maxLen);
}
longestPalindrome('babad');

死记硬背系列,

最长回文子序列

/**
 * 示例 1: 输入: "bbbab" 输出: 4 一个可能的最长回文子序列为 "bbbb"
 * 示例 2: 输入:"cbbd" 输出: 2 一个可能的最长回文子序列为 "bb"。
 */
function longestPalindSub(s) {
  let len = s.length;
  let dp = new Array(len).fill().map(() => new Array(len).fill(0));
  // 对角线部分
  for (let i = 0; i < len; i++) {
    dp[i][i] = 1;
  }
  // 从上往下 从左往右 右上角最大
  // 遍历i的时候一定要从下到上遍历,这样才能保证下一行的数据是经过计算的
  for (let i = len - 1; i >= 0; i--) {
    for (let j = i + 1; j < len; j++) {
      if (s[i] == s[i]) {
        dp[i][j] = dp[i + 1][j - 1] + 2; // +2
      } else {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
      }
    }
  }
  return dp[0][len - 1];
}
longestPalindSub('cbbd');

最长有效括号

/**
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
 */
function longestValidPar(str) {
  if (str.length < 2) return 0;
  let stack = [-1];
  let max;
  for (let i = 0; i < str.length; i++) {
    let cur = str[i];
    if (cur == '(') {
      stack.push(i);
    } else {
      stack.pop();
      if (stack.length == 0) {
        stack.push(i);
      } else {
        max = Math.max(max, i - stack.slice(-1));
      }
    }
  }
  return max;
}

最长公共前缀

/**
 * 输入:strs = ["flower","flow","flight"]
输出:"fl"
 */
// 横向扫描
function findLongestPefix(nums) {
  let prefix = nums[0];
  for (let i = 1; i < nums.length; i++) {
    let cur = nums[i];
    prefix = findTwo(cur, prefix);
    if (prefix.length == 0) {
      continue;
    }
  }
  return prefix;
}
function findTwo(a, b) {
  let len = Math.min(a.length, b.length);
  let i = 0;
  let j = len;
  while (i <= len && a[i] == b[i]) {
    i++;
  }
  return a.slice(0, i);
}
findLongestPefix(['flower', 'flow', 'flight']);

无重复字符的最长子串

/**
 * 输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
 */
function longestSub(str) {
  let max = 0;
  let ans = [];
  for (let i = 0; i < str.length; i++) {
    let cur = str[i];
    const index = ans.indexOf(cur);
    if (index > -1) {
      ans.splice(0, index + 1);
    }
    ans.push(cur);
    max = Math.max(max, ans.length);
  }
  return max;
}