Strings · Medium
53. Longest Repeating Subsequence
Topics: String, Dynamic Programming
Solve it on DeVert Campus - free code runner
Problem
Given a string s, find the length of its longest subsequence that appears at least twice in s using different index positions for each occurrence (the same character at the same index cannot be reused across the two copies).
Input and output
Input: The input is a single line containing the string s.
Output: Print a single integer - the length of the longest repeating subsequence.
Example
Input: aabb Output: 2
Constraints
1 <= |s| <= 2000
Key observation
Without the i != j restriction, comparing a string to itself would trivially just match every character with itself, giving an answer equal to the string's own length - the restriction is what makes this a genuinely different, harder question.
Intuition
Find the longest subsequence of a string that appears TWICE within it, using genuinely different character positions each time (not reusing the exact same index for both copies).
Brute force: Try every possible pair of subsequences (using two different sets of indices) directly and check if they're equal - this is exponential, completely impractical beyond tiny strings.
Optimised: Reframing the problem as 'LCS of the string with itself, but forbidding matched-index pairs' lets you reuse the EXACT same DP recurrence as standard LCS, with just one small added condition (i != j) - no need to invent a new algorithm from scratch, just a clever adaptation of a known one.
Optimal - Bottom-Up DP with Rolling Array
Fill the same DP table iteratively from smaller indices upward instead of via recursion, avoiding recursion-call overhead and any risk of stack depth issues for |s| up to 2000. Furthermore, since row i of the table only ever depends on row i-1, there's no need to keep the full 2D table in memory - keep just two 1D rows (previous and current) and roll them forward as i increases, reading off the final answer once the last row is computed. This keeps the same O(n^2) time as the memoized version but reduces the memory footprint from quadratic to linear.
Time: O(n^2)
Space: O(n)
Approach 2
Define the recurrence LRS(i, j) comparing the string against itself: if s[i] == s[j] and i != j, this character can be part of a shared subsequence, so LRS(i,j) = 1 + LRS(i-1, j-1); otherwise LRS(i,j) = max(LRS(i-1, j), LRS(i, j-1)). Implemented as plain recursion with no memoization, this correctly enumerates the choices but recomputes the same (i, j) subproblems repeatedly through different recursive paths, causing exponential blowup.
Time: O(2^n) (exponential)
Space: O(n)
Approach 3
The recursion above only ever depends on the pair (i, j), so store each computed LRS(i, j) result in a memo table indexed by i and j (top-down memoization) so it is computed only once. Every distinct (i, j) pair, of which there are O(n^2), now does O(1) work beyond its two recursive lookups, turning the exponential brute force into a quadratic algorithm - a dramatic improvement given |s| can be up to 2000.
Time: O(n^2)
Space: O(n^2)
Step by step
1. This is exactly the Longest Common Subsequence of the string WITH ITSELF, but with one extra rule: matching characters can't come from the same index in both copies.
2. Build a DP table dp[i][j] comparing the string against itself: if s[i-1] equals s[j-1] AND i is not equal to j, extend the match; otherwise take the best of skipping a character from either 'copy'.
3. The final answer is in the bottom-right cell of the table.
Complexity
Time: O(n²) - filling the DP table, exactly like standard LCS, since the string is being compared against itself.
Space: O(n²), for the DP table.
Edge cases
A string with all distinct characters (answer 0, no repeat possible); a string of all identical characters, which stresses the i != j index constraint; s up to length 2000, testing O(n^2) DP performance.
Interview tip
Be ready to immediately recognize and state the 'LCS of s with itself, forbidding i==j matches' framing - this is exactly the kind of pattern-recognition (spotting a known algorithm hiding inside a new-sounding problem) interviewers reward.