CMD Guide
HomeDSADynamic Programming

Longest Palindromic Subsequence

LPS asks: what is the longest subsequence of a string that reads the same forward and backward, and it works by shrinking the string from both ends — if the outer characters match, they can anchor a palindrome around whatever palindrome exists inside; if they don't match, the best palindrome must sacrifice one end or the other, so you take the better of the two smaller subproblems.

Recognize the pattern

Brute force → optimal

Brute force: generate all 2^n subsequences, check each for being a palindrome, keep the longest. Cost: O(2^n · n) time, O(n) space (recursion) — infeasible past n≈20.

Optimal (interval DP): define dp[i][j] = length of the LPS within s[i..j] (inclusive). For every substring range, decide based on whether the two boundary characters match:

dp[i][j] =
  1                                  if i == j
  2 + dp[i+1][j-1]                   if i < j and s[i] == s[j]
  max(dp[i+1][j], dp[i][j-1])        if i < j and s[i] != s[j]

Base case: single character is a palindrome of length 1; empty range (i>j) has length 0.

Complexity, derived

The state space is every pair (i, j) with 0 ≤ i ≤ j < n, roughly n²/2 distinct subproblems. Each state does O(1) work (a comparison plus one or two array lookups), so total time is O(n²). Because dp[i][j] only depends on smaller ranges (i+1,j-1, i+1,j, i,j-1), filling the table by increasing range length len = j-i guarantees dependencies are already computed. Space for the full table is O(n²); it can be compressed to O(n) with two rolling rows if only the length (not the palindrome text) is needed, since each row only reads the row below it plus one earlier cell in the same row.

Worked example: s = "cddpd" (n=5)

Indices: c(0) d(1) d(2) p(3) d(4). Fill by increasing length.

rangesubstrings[i]==s[j]?dp[i][j]
len0each single char-1
(0,1)cdnomax(dp[1][1],dp[0][0])=1
(1,2)ddyes2+dp[2][1](empty=0)=2
(2,3)dpno1
(3,4)pdno1
(1,3)ddpno (d≠p)max(dp[2][3],dp[1][2])=max(1,2)=2
(2,4)dpdyes (d==d)2+dp[3][3]=2+1=3
(0,2)cddnomax(dp[1][2],dp[0][1])=max(2,1)=2
(1,4)ddpdyes (d==d)2+dp[2][3]=2+1=3
(0,3)cddpnomax(dp[1][3],dp[0][2])=max(2,2)=2
(0,4)cddpdno (c≠d)max(dp[1][4],dp[0][3])=max(3,2)=3

Answer: dp[0][4] = 3, matching "ddd".

Java implementation

class Solution {
    public int longestPalinSubseq(String s) {
        int n = s.length();
        if (n == 0) return 0;
        int[][] dp = new int[n][n];
        for (int i = 0; i < n; i++) dp[i][i] = 1;
        for (int len = 2; len <= n; len++) {
            for (int i = 0; i + len - 1 < n; i++) {
                int j = i + len - 1;
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = 2 + (len == 2 ? 0 : dp[i + 1][j - 1]);
                } else {
                    dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[0][n - 1];
    }
}

Pitfalls

When to use / when not — trade-offs

Use interval DP when the answer decomposes by shrinking both ends of a range and the match/mismatch of the endpoints determines the recurrence (also used in palindrome partitioning, burst balloons, matrix chain multiplication). Space is O(n²), which is expensive for very large n (say n > 10⁴, giving 10⁸ cells) — if only the count and not the reconstructed string is needed, use the O(n) rolling-row optimization. As an alternative, LPS(s) = LCS(s, reverse(s)) reduces it to the more general Longest Common Subsequence algorithm — same O(n²) time/space, useful if you already have an LCS routine, but it costs roughly 2x the memory/compute of the direct interval DP if you only need this one problem solved.

Takeaways

Recall: Why must the DP table for LPS be filled in order of increasing substring length rather than by row index?


Adapted and expanded from the interview-prep source problem "Longest Palindromic Subsequence" with derivation, worked trace, complexity analysis, and trade-off discussion added by the study guide.

🤖 Don't fully get this? Learn it with Claude

Stuck on Longest Palindromic Subsequence? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Longest Palindromic Subsequence** (DSA) and want to truly understand it. Explain Longest Palindromic Subsequence from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Longest Palindromic Subsequence** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Longest Palindromic Subsequence** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Longest Palindromic Subsequence** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes