💡 Memoization avoids recomputing the same subproblems by caching results, drastically improving efficiency.
Intuition
Use recursion like brute force but store results of subproblems in a table to reuse them when needed.
Algorithm
- Initialize a 2D cache with -1 indicating uncomputed states.
- Recursively compute LCS length for prefixes, returning cached results if available.
- If characters match, add 1 plus result from smaller prefixes.
- Otherwise, take max of skipping one character from either string.
💡 Memoization transforms exponential recursion into polynomial time by avoiding repeated work.
Recurrence:Same as brute force: lcs(i, j) = 1 + lcs(i-1, j-1) if match else max(lcs(i-1, j), lcs(i, j-1))
def lcs(text1, text2):
m, n = len(text1), len(text2)
memo = [[-1] * (n + 1) for _ in range(m + 1)]
def dfs(i, j):
if i == 0 or j == 0:
return 0
if memo[i][j] != -1:
return memo[i][j]
if text1[i - 1] == text2[j - 1]:
memo[i][j] = 1 + dfs(i - 1, j - 1)
else:
memo[i][j] = max(dfs(i - 1, j), dfs(i, j - 1))
return memo[i][j]
return dfs(m, n)
# Driver code
print(lcs("abcde", "ace"))
Line Notes
memo = [[-1] * (n + 1) for _ in range(m + 1)]Initialize memo table with -1 to mark unvisited states
if memo[i][j] != -1:Return cached result if already computed
if text1[i - 1] == text2[j - 1]:Check if characters match to extend subsequence
memo[i][j] = max(dfs(i - 1, j), dfs(i, j - 1))Store max result after skipping one character
public class Solution {
public static int lcs(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] memo = new int[m + 1][n + 1];
for (int i = 0; i <= m; i++)
for (int j = 0; j <= n; j++)
memo[i][j] = -1;
return dfs(text1, text2, m, n, memo);
}
private static int dfs(String text1, String text2, int i, int j, int[][] memo) {
if (i == 0 || j == 0) return 0;
if (memo[i][j] != -1) return memo[i][j];
if (text1.charAt(i - 1) == text2.charAt(j - 1))
memo[i][j] = 1 + dfs(text1, text2, i - 1, j - 1, memo);
else
memo[i][j] = Math.max(dfs(text1, text2, i - 1, j, memo), dfs(text1, text2, i, j - 1, memo));
return memo[i][j];
}
public static void main(String[] args) {
System.out.println(lcs("abcde", "ace"));
}
}
Line Notes
int[][] memo = new int[m + 1][n + 1];Create memo table for caching
if (memo[i][j] != -1) return memo[i][j];Return cached result if available
if (text1.charAt(i - 1) == text2.charAt(j - 1))Check for character match
memo[i][j] = Math.max(...)Store max result after skipping characters
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int dfs(const string &text1, const string &text2, int i, int j, vector<vector<int>> &memo) {
if (i == 0 || j == 0) return 0;
if (memo[i][j] != -1) return memo[i][j];
if (text1[i - 1] == text2[j - 1])
memo[i][j] = 1 + dfs(text1, text2, i - 1, j - 1, memo);
else
memo[i][j] = max(dfs(text1, text2, i - 1, j, memo), dfs(text1, text2, i, j - 1, memo));
return memo[i][j];
}
int lcs(const string &text1, const string &text2) {
int m = text1.size(), n = text2.size();
vector<vector<int>> memo(m + 1, vector<int>(n + 1, -1));
return dfs(text1, text2, m, n, memo);
}
int main() {
cout << lcs("abcde", "ace") << endl;
return 0;
}
Line Notes
vector<vector<int>> memo(m + 1, vector<int>(n + 1, -1));Initialize memo table with -1
if (memo[i][j] != -1) return memo[i][j];Return cached result if computed
if (text1[i - 1] == text2[j - 1])Check character match
memo[i][j] = max(...)Store max LCS length after skipping characters
function lcs(text1, text2) {
const m = text1.length, n = text2.length;
const memo = Array.from({ length: m + 1 }, () => Array(n + 1).fill(-1));
function dfs(i, j) {
if (i === 0 || j === 0) return 0;
if (memo[i][j] !== -1) return memo[i][j];
if (text1[i - 1] === text2[j - 1]) {
memo[i][j] = 1 + dfs(i - 1, j - 1);
} else {
memo[i][j] = Math.max(dfs(i - 1, j), dfs(i, j - 1));
}
return memo[i][j];
}
return dfs(m, n);
}
console.log(lcs("abcde", "ace"));
Line Notes
const memo = Array.from({ length: m + 1 }, () => Array(n + 1).fill(-1));Create 2D memo array initialized to -1
if (memo[i][j] !== -1) return memo[i][j];Return cached result if available
if (text1[i - 1] === text2[j - 1]) {Check if characters match
memo[i][j] = Math.max(dfs(i - 1, j), dfs(i, j - 1));Store max LCS length after skipping characters
Each subproblem (i,j) is computed once and stored, reducing exponential calls to polynomial.
💡 For strings of length 1000, this means about 1 million computations, which is feasible.
Interview Verdict: Accepted
Memoization makes the solution efficient enough for typical interview constraints.