【问题标题】:Longest Palindromic Substring. What could be the run time for the below DP + Recursive approach. Is it O(n^2) or O(n^3)最长回文子串。以下 DP + Recursive 方法的运行时间可能是多少。是 O(n^2) 还是 O(n^3)
【发布时间】:2020-04-13 00:35:32
【问题描述】:
class Solution {

public String longestPalindrome(String s) {
    if(s.length() == 0){
        return "";
    }
    String[][] dp = new String[s.length()][s.length()];
    int n = s.length();
    for(int i=0; i<n; i++){
        dp[i][i] = s.substring(i,i+1);
    }

    int l = n/2;
    int r = n/2;
    while(!(l==0 && r==n-1)){
        if(l>0){
            l--;
        }
        if(r<n-1){
            r++;
        }
        longest(l, r, s, dp);
    }
    return dp[0][n-1];
}

public String longest(int l, int r, String s, String[][] dp) {
    if(dp[l][r] != null){
        return dp[l][r];
    } else if(s.charAt(l) == s.charAt(r) && (l == r-1 || longest(l+1, r-1, s, dp).length() == ((r-1) - (l+1))+1)){
        dp[l][r] = s.substring(l,r+1);
        return dp[l][r];
    } else {
        String l1 = longest(l, r-1, s, dp);
        String l2 = longest(l+1, r, s, dp);
        dp[l][r] = l1.length() > l2.length() ? l1 : l2;
        return dp[l][r];
    }
   }
  }

dp[i][j] 表示索引 i,j 之间的最大回文串。我们最终会返回 dp[0][s.length()-1]

代码分析:

如果字符串(比如 b)的长度为 1,我们可以从中得到的最大回文数将是字符串本身(b)。现在让我们将字符串扩展到后面的 +b+。如果将 + 符号替换为某些字母,最长的回文字符串将是什么。我们可以考虑两种可能性: i) 两个字母相同 ii)它们都是不同的。 如果 case i --> 我们可以说整个字符串是回文,因为我们知道字母之间的字符串完全是回文。 如果情况 ii --> 我们将有两种可能性考虑左而忽视右或考虑右而忽视左。我们正在寻找的那个是产生最大回文字符串的那个。

基于这个想法,我们将填充整个 dp 数组。

我知道算法的空间复杂度是 O(n^2),但运行时复杂度是多少。是 O(n^2) 还是 O(n^3)?

【问题讨论】:

  • 我认为您的算法不起作用。但是你的方法是围绕中心展开,这需要 O(n^2) 时间复杂度
  • @Melchia 它确实有效,并进行了完美的验证。是的,它正在围绕中心扩展。
  • “完美验证”没有任何意义。你有证据吗?

标签: algorithm performance


【解决方案1】:

关于算法的复杂性,您可能需要计算计算dp[l][r] 的次数(因此避免已经缓存的情况)

  • 对于像 'a'.repeat(10^4) 这样非常大的字符串,请检查您的计数。
  • 然后对两倍大 'a'.repeat(2*10^4) 的字符串执行此操作,并检查您的计数是 4x 还是 2^3x。

也就是说我们可以不用递归(而且看起来更容易)

如果一个长度为 n 的字符串是回文,那么长度为 n-2 的字符串也被左右截断 1 个字符。

算法

回文的长度要么是偶数,要么是奇数

  • 获取所有 1 字符串和所有 2 字符串(在后一种情况下使用相同的字符)
  • 要构建给定字符串的后继,请检查左右是否相同
  • 只要您有继任者,就继续前进

复杂性

让我们专注于奇数回文。

假设我们的字符串是偶数长度。

  • 我们有 n 个长度为 1 的候选者。
  • 我们可以构建 n-2 个下一个(长度为 3)
  • 直到我们只有两个长度为 n-1 的候选者

总计:n + n-2 + ... + 0 = sum_k=0^{n/2} (n - 2k) = n*n/2 - (n/2*(n/2+1)) ~ O(n^2)

对于偶数长度的回文,我们可以将其加倍,因此复杂度保持在 O(n^2)

function pal (n) {
  console.time('go')
  const s = 'a'.repeat(n)
  let candidates = s.split('').map((c, i) => ({ l: i, r: i }))
  s.split('').forEach((c, i) => c === s[i + 1] && candidates.push({ l: i, r: i + 1 }))
  while(candidates.length) {
    const next = []
    candidates.forEach(({ l, r }) => {
      if (l - 1 >= 0 && r + 1 < s.length && s[l - 1] === s[r + 1]) {
        next.push({ l: l - 1, r: r + 1 })
      }
    })
    if (next.length === 0) {
      // print the the biggest ones, start from the bottom...
      const best = candidates[candidates.length - 1]
      const bestLength = best.r - best.l
      candidates.filter(c => c.r - c.l === bestLength).forEach(c => console.log(c))
    }
    candidates = next
  }
  console.timeEnd('go')
}
const base = 0.2*10**4
pal(base) // (got 1.010s with base = 0.5**10^4 but this freezes UI, so... adjust yourself to have a big enough number)
pal(base*2) // expect 4 time slower (got 4.163s)
pal(base*4) // expect 16 time slower (got 16.819s)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 1970-01-01
    • 2021-10-22
    • 1970-01-01
    • 1970-01-01
    • 2019-11-21
    • 1970-01-01
    相关资源
    最近更新 更多