dp dp[i][j] = LCS of text1.substring(i) and text2.substring(j) · ∅ = nothing left
public int longestCommonSubsequence(String text1, String text2) { int m = text1.length(), n = text2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = m - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { if (text1.charAt(i) == text2.charAt(j)) { dp[i][j] = 1 + dp[i + 1][j + 1]; } else { dp[i][j] = Math.max(dp[i][j + 1], dp[i + 1][j]); } } } return dp[0][0];}
- i, j
- —
- text1.charAt(i)
- —
- text2.charAt(j)
- —
- dp[i][j]
- —
Why it works
- Brute force lists every subsequence of one string (2m of them) and checks each against the other in O(n): O(n·2m).
- Look at the first characters. If
text1.charAt(i) == text2.charAt(j), some longest answer uses that pair, so take it and move both:1 + dp[i+1][j+1]. If not, one of them is useless here: drop either and keep the better,max(dp[i][j+1], dp[i+1][j]). - Fill from the bottom-right. Each cell reads the cell to its right, below, and diagonally below-right, all filled earlier. (m+1)·(n+1) cells, O(1) each: O(m·n) time and space. Keeping only two rows cuts space to O(n).
- Pitfall: the extra ∅ row and column. They stand for an empty suffix, whose LCS with anything is 0, and they keep
i + 1andj + 1inside the table. - Getting the subsequence itself: walk from
dp[0][0]. On a match take the character and go diagonal; otherwise step toward the larger neighbour. The green path in the last step does exactly that. - Related: Edit Distance (72) and Distinct Subsequences (115) use the same two-suffix table with a different rule per cell.