4

I'm trying to implement a function that takes a string as input and returns the longest palindrome subsequence in the string.

I've tried using dynamic programming and have come up with the following code:

function longestPalindromicSubsequence(str) {
  let n = str.length;
  let dp = Array(n);
  for (let i = 0; i < n; i++) {
    dp[i] = Array(n);
    dp[i][i] = 1;
  }
  for (let cl = 2; cl <= n; cl++) {
    for (let i = 0; i < n - cl + 1; i++) {
      let j = i + cl - 1;
      if (str[i] === str[j] && cl === 2)
        dp[i][j] = 2;
      else if (str[i] === str[j])
        dp[i][j] = dp[i + 1][j - 1] + 2;
      else
        dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
    }
  }
  return dp[0][n - 1];
}

However, this code doesn't seem to be giving me efficient and better results for all test cases. The Time and Space Complexity is also be reduced. I've been struggling with this for days and can't seem to find the issue. Can someone help me figure out what's going wrong and how to fix it?

Michael M.
  • 10,486
  • 9
  • 18
  • 34
  • `longestPalindromicSubsequence("abcda") -> 3` I don't think it is efficiency you have to worry about at this moment. How about correctness? – teapot418 Feb 12 '23 at 11:36
  • I think one question to contemplate is what the items in `dp` represent. And if you should really increase them by 2 for the **a**bcd**a** case. – teapot418 Feb 12 '23 at 11:46

2 Answers2

1

Oh, I think Dynamic Programming does not work with this sort of problem, because it does not break down recursively, i.e. to find the longest palindrome in a string, you don't need all second-largest palindromes. You can just check at each position and see if it is the center of a palindrome longer than any before. This can be solved with a greedy algorithm:

const pals = "asd1234321fghjkl1234567887654321qwertzu1234321"

function palindromeAtPos(str, pos, checkEven = false){
  let ix = 0
  const off = checkEven ? 2 : 1
  while(pos-ix-1 >= 0 && pos+ix+1+off < str.length && str[pos-ix-1] === str[pos+ix+off]){
    ix++
  }
  return ix === 0 ? str[pos] : str.substring(pos-ix, pos+ix+off)
}

function longestPalindrome(str){
  let longest = ''
  for(let i = 1; i < str.length; i++){
    const odd = palindromeAtPos(str, i)
    longest = odd.length > longest.length ? odd : longest
    const even = palindromeAtPos(str, i, true)
    longest = even.length > longest.length ? even : longest
  }
  return longest
}

console.log(longestPalindrome(pals))

On paper (and for a string like aaaaaaaaaa), this has quadratic complexity, but for most strings, it will be almost linear.

Moritz Ringler
  • 9,772
  • 9
  • 21
  • 34
-1

/*
*   s => string
*   return [] of strings witch have the max lenth
*/
function maxLenPalindromes(s) {
   const l = s.length
   let c, z, zz, a, b, a1, b1, maxl = 0, result = []
   if (l < 2) return result
   for (c = 0; c < l - 1; c++) {
      a = -1
      if (maxl>(l-c)*2+1) return result
      if (c > 0 && s[c - 1] == s[c + 1]) {
         zz = Math.min(c, l - c - 1)
         for (z = 1; z <= zz; z++) {
            if (s[c - z] != s[c + z]) {
               a = c - z + 1; b = c + z
               break
            }
            else if (z == zz) {
               a = c - z; b = c + z + 1
               break
            }
         }
         if (a >= 0) {
            if (b-a > maxl) {
               result = [s.slice(a, b)]
               maxl = b-a
            }
            else if (b-a == maxl) {
               result.push(s.slice(a, b))
            }
         }         
      }
      a=-1
      if (s[c] == s[c + 1]) {
         if (c == 0 || c == l - 2) {
            a = c; b = c + 2
         }
         else {
            zz = Math.min(c, l - c - 2)
            for (z = 1; z <= zz; z++) {
               if (s[c - z] != s[c + z + 1]) {
                  a = c - z + 1; b = c + z + 1
                  break
               }
               else if (z == zz) {
                  a = c - z; b = c + z + 2
                  break
               }
            }

         }
         if (a >= 0) {
            if (b-a > maxl) {
               result = [s.slice(a, b)]
               maxl = b-a
            }
            else if (b-a == maxl) {
               result.push(s.slice(a, b))
            }
         } 
      }      
   }
   return result
}

const s1="112233111222333"
const s2="11_22_33_111_222_333"
const s3="12345_54321xqazws_swzaq_qwertytrewq"
const s4="sdfgsdfg1qqqqqAAAAA_123456789o o987654321_AAAAAqqqqq;lakdjvbafgfhfhfghfh"
console.log(maxLenPalindromes(s1))
console.log(maxLenPalindromes(s2))
console.log(maxLenPalindromes(s3))
console.log(maxLenPalindromes(s4))