Q:
Given a string s, find the longest palindromic substring in s.
A string is a palindrome if it reads the same backward as forward.
Input:
1 | Input: s = "babad" |
Solution:
If this is your first time encountering such a problem, it might not be easy to immediately think of dynamic programming.
However, with step-by-step reasoning and optimization, DP naturally emerges as a clean solution.
Step 1: Naive Approach
The simplest idea is:
Design a helper method
judgeIsPalindrome.
Using a two-pointer technique, we can check whetherString(i, j)is a palindrome in O(n).Enumerate all substring lengths
k, from large to small.
For each length, check all substringsString(i, i+k-1).
If a palindrome is found, return it as the result.
This works, but it’s inefficient.
- We must enumerate all
kvalues (worst case: N). - For each
k, we may check up to N substrings. - This leads to high complexity, which is not feasible.
We need something more efficient.
Step 2: Binary Search Optimization
Instead of enumerating all possible lengths k, we could binary search for the maximum valid k.
This reduces the search to O(log N).
(Interested readers can try implementing this approach.)
However, there’s still redundancy: each new substring is checked independently, without reusing previously computed results.
Step 3: Dynamic Programming Insight
Let’s improve by leveraging earlier computations.
Suppose for some length k_i, we already know whether every substring of length k_i is a palindrome.
Now, consider extending a substring:
If we know String(i, i+k_i-1) is a palindrome, then checking String(i-1, i+k_i) can be done in O(1):
1 | dp[i][j] = dp[i+1][j-1] && (s[i] == s[j]) |
That is:
- If the inner substring
String(i+1, j-1)is a palindrome, - and the boundary characters match (
s[i] == s[j]), - then
String(i, j)is also a palindrome.
DP Implementation
1 | public String longestPalindrome(String s) { |
Summary
- Naive approach: brute force with palindrome checking → O(N³).
- Optimization: binary search on substring length → O(N² log N).
- Dynamic Programming: reuse previously computed results → O(N²).
This step-by-step refinement shows how dynamic programming becomes a natural and efficient solution.