The subsequence problem is a common algorithm problem, and it is not easy to solve.
First of all, the subsequence problem itself is more difficult than substrings and subarrays, because the former is a discontinuous sequence, while the latter two are continuous. Even if you exhaustively list, you may not know how to solve the related algorithm problems. .
Moreover, the subsequence problem is likely to involve two strings, such as the "longest common subsequence" in the previous article. It is really not easy to come up with it without certain processing experience. So this article is going to take a look at the routine of the sub-sequence problem. In fact, there are two templates. As long as you think about these two ideas, you can be sure of the relevant problems.
Generally speaking, this kind of question asks you to find the longest subsequence , because the shortest subsequence is just a character, there is nothing to ask. Once the subsequence and the maximum value are involved, it is almost certain that the dynamic programming technique is examined, and the time complexity is generally O(n^2) .
The reason is simple. Think about a string. How many possible subsequences are there? At least it's exponential. In this case, you don't need dynamic programming skills. What do you want?
Since we want to use dynamic programming, we must define the dp array to find the state transition relationship. The two thinking templates we are talking about are the definition of dp array. Different problems may require different dp array definitions to solve.
One or two ideas
1. The first idea template is a one-dimensional dp array :
int n = array.length; int[] dp = new int[n]; for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { dp[i] = 最值(dp[i], dp[j] + ...) } }
For example, the "longest increasing subsequence" we wrote, the definition of dp array in this idea is:
In the sub-array array[0..i]
, the length of the sub-sequence (the longest increasing sub-sequence) we require is dp[i]
.
Why does the longest increasing subsequence need this kind of thinking? The previous article has made it very clear, because this conforms to the induction method and can find the relationship between the state transitions, and I will not expand it in detail here.
2. The second idea template is a two-dimensional dp array :
int n = arr.length; int[][] dp = new dp[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (arr[i] == arr[j]) dp[i][j] = dp[i][j] + ... else dp[i][j] = 最值(...) } }
This kind of thinking is used relatively more, especially involving two subsequences of strings/arrays, such as the "longest common subsequence" mentioned above. The meaning of the dp array in this idea is divided into two cases: "only one string is involved" and "two strings are involved".
PS: I have written more than 100 original articles carefully , and I have hand-in-hand brushed with 200 buckle questions, all of which are published in labuladong's algorithm cheat sheet , which is continuously updated . It is recommended to collect, brush the questions in the order of my articles , master various algorithm routines, and then cast them into the sea of questions.
2.1 When two strings/arrays are involved (such as the longest common subsequence), the meaning of the dp array is as follows:
In sub-arrays arr1[0..i]
and sub-arrays arr2[0..j]
, the length of the sub-sequence (the longest common sub-sequence) we require is dp[i][j]
.
2.2 When only one string/array is involved (such as the longest palindrome subsequence described in this article), the meaning of the dp array is as follows:
In the sub-array array[i..j]
, the length of the required sub-sequence (the longest palindrome sub-sequence) is dp[i][j]
.
For the first case, you can refer to these two old articles: "Edit Distance" and "Common Subsequence"
Let's borrow the problem of the longest palindrome subsequence to explain in detail how to use dynamic programming in the second case.
2. The longest palindrome subsequence
The problem of "the longest palindrome substring" was previously solved. This time the difficulty is increased to find the length of the longest palindrome subsequence:
We say that the definition of the dp array in this question is: in the substring s[i..j]
, the length of the longest palindrome subsequence is dp[i][j]
. You must remember this definition to understand the algorithm.
Why does this problem define a two-dimensional dp array like this? We mentioned many times in the previous article that finding state transition requires inductive thinking. To put it bluntly, it is how to deduce the unknown part from the known results . This definition is easy to generalize and easy to find the state transition relationship.
Specifically, if we want to find dp[i][j]
, suppose you know the dp[i+1][j-1]
result of the sub-problem ( s[i+1..j-1]
the length of the longest palindrome subsequence), can you figure out dp[i][j]
the value ( s[i..j]
the length of the longest palindrome subsequence)?
can! It depends s[i]
and s[j]
characters:
If they are equal , then s[i+1..j-1]
the longest palindrome subsequence in the two additions is s[i..j]
the longest palindrome subsequence:
If they are not equal , it means that they are unlikely to appear in s[i..j]
the longest palindrome subsequence at the same time , then add them separately to s[i+1..j-1]
see which substring produces the longer palindrome subsequence:
The above two cases are written in code like this:
if (s[i] == s[j]) // They must be in the longest palindrome subsequence dp[i][j] = dp[i + 1][j-1] + 2; else // Which palindrome subsequence of s[i+1..j] and s[i..j-1] is longer? dp[i][j] = max(dp[i + 1][j], dp[i][j-1]);
At this point, the state transition equation has been written. According to the definition of the dp array, what we require is the length of the dp[0][n - 1]
entire s
longest palindrome subsequence.
Three, code implementation
First clarify the base case. If there is only one character, obviously the length of the longest palindrome subsequence is 1, that is dp[i][j] = 1 (i == j)
.
Because it i
must be less than or equal j
to those i > j
positions, there is no subsequence at all and should be initialized to 0.
In addition, take a look at just written state transition equation, I would like to ask dp[i][j]
you need to know dp[i+1][j-1]
, dp[i+1][j]
, dp[i][j-1]
the three positions; take a look at our base case determined, after fill dp array like this:
In order to ensure that dp[i][j]
the position in the lower left and right direction has been calculated for each calculation , it can only be traversed diagonally or reversely :
I chose to traverse the other way, the code is as follows:
int longestPalindromeSubseq(string s) { int n = s.size(); // all dp arrays are initialized to 0 vector<vector<int>> dp(n, vector<int>(n, 0)); // base case for (int i = 0; i <n; i++) dp[i][i] = 1; // Reverse traversal to ensure correct state transition for (int i = n-1; i >= 0; i-- ) { for (int j = i + 1; j <n; j++) { // State transition equation if (s[i] == s[j]) dp[i][j] = dp[i + 1] [j-1] + 2; else dp[i][j] = max(dp[i + 1][j], dp[i][j-1]); } } // the longest return of the entire s The length of the text string return dp[0][n-1]; }
So far, the problem of the longest palindrome subsequence is solved.
_____________