[Linear dp] | AcWing algorithm basic class test questions summary (2)

897. Longest Common Subsequence

Title description

Given two strings A and B with lengths N and M respectively, find the longest string length that is both a subsequence of A and a subsequence of B.

Input format The
first line contains two integers N and M.
The second line contains a string of length N, which represents the string A.
The third line contains a string of length M, which represents the string B.

The string is composed of lowercase letters.

Output format
Output an integer, indicating the maximum length.

Data range
1≤N, M≤1000

Input sample:

4 5
acbd
abedc

Sample output:

3

Code

Idea One

Set representation : f[i,j] represents the longest common subsequence length of the first i characters of a and the first j characters of b. (The longest common subsequence of two prefix substrings)

Set division : Based on whether a[i] and b[j] are included in the subsequence, it can be divided into four categories:

a[i] is absent, b[j] is absent :f [i−1,j−1]

a[i] is not there, b[j] is :f[i−1,j]

It seems to be f[i−1,j], but in fact it cannot be expressed by f[i−1,j]. Because f[i−1,j] represents the length of the longest common subsequence between the first i-1 letters of a and the first j letters of b, but this is the largest that satisfies the condition f[i-1,j] The subsequence does not necessarily include b[j], and what we require is that a[i] is not in the subsequence and b[j] is in the subsequence, find its length. The two are not exactly equal.

But it can still be represented by f[i−1,j], the reason is that a[i] is not in the subsequence and b[j] is in the subsequence is included in f[i−1,j], That is, this case is a subset of the case of f[i−1,j], and its maximum value is less than or equal to the maximum value of f[i -1,j]. The other subset is that a[i] is absent and b[j] is also If they are not in the subsequence f [i−1,j−1], the two are combined to form the complete set f[i-1,j].

Although the maximum value of f[i-1,j] is not necessarily the case that the subsequence contains b[j], it has no effect on the answer, because the scheme in f[i-1,j] must be f[ The scheme in i, j], as long as it includes the case of choosing b[j].

The value of f [i − 1, j] can be divided into two parts: if b [j] is not selected and b [j] is selected, it is max (f [i − 1, j − 1], if b [j] is selected Situation) The value of f[i-1,j] can be divided into two parts: do not choose b[j] and choose b[j], which is max(f[i-1,j-1], choose b[j ]Case) f[i. 1 , J ] the value may be in divided as two portion points configured to : not selected from the group B [ J ] and selected from the group B [ J ] , it is m A X ( F [ I1j1 ] , selected from the group B [ J ] in the case condition )

For example: the maximum value of a, b, c can be calculated as follows: max( max(a,b), max(b,c)) Although b is used repeatedly, max can still be obtained. As long as max is not missed, That's it.

a[i] is there, b[j] is not :f[i,j-1], The principle is the same as ②

a[i] is in, b[j] is in :f[i−1,j−1]+1; Prerequisite: a[i]==b[j]

Because ① has been included in the cases of ② and ③ in the calculation, ① does not need to be considered. And situation ② and situation ③ are inevitable.

That is, for a two-dimensional table f, each time f[i,j] is calculated, three directions are used at most, left f[i-1,j], upper f[i,j-1] and upper left f[ i-1, j-1] , the left and top will be used inevitably, and the upper left must have a prerequisite: a[i]==b[j]

Initialization situation:

Since i-1 and j-1 are involved, it is better to store the f array starting from subscript 1, so it is better to store related strings starting from subscript 1.

The processing starts from the first character, so it is necessary to preprocess the “0th character” first : both the 0th row and the 0th column should be initialized to 0 .

That is, any string matching a null string is 0.

When i=1, f[0,j] in each column is used; when j=1, f[i,0] in each row is used;

When i=1, j=1 and a[i]=b[j], f[0, 0] is used

#include <iostream>
#define read(x) scanf("%d",&x)

using namespace std;

const int N=1010;
char a[N],b[N];
int f[N][N];

int main()
{
    
    
    int n,m;
    read(n),read(m);
    scanf("%s%s",a+1,b+1);
    
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++) {
    
    
            f[i][j]=max(f[i-1][j],f[i][j-1]);
            if (a[i]==b[j])  f[i][j]=max(f[i][j],f[i-1][j-1]+1);
        }
    
    printf("%d",f[n][m]);
    
    return 0;
}

Since f[i,j] is calculated, it will be used later, as the three directions of left, top, and top left are used three times, so it is unlikely to use only 4 variables to represent left, top, and The upper left and the answer to optimize the space complexity.

Idea two

Idea 2 and Idea 1 look similar, but the actual way of thinking is still different, and the meaning of the code is also different.

Consider a question: When a[i]==b[j], do we have to match them as part of the common subsequence?

The following ideas are all carried out on the basis of the establishment of this condition.

According to whether the characters at the end of the two sequences are equal to distinguish.
If the two characters are equal, they can be directly transferred to f[i-1, j-1]; if
they are not equal, one of the two characters must be discarded, and f[i-1, j], f[i, j-1] The two states take max to transfer.

#include <iostream>

using namespace std;

const int N = 1010;
char a[N], b[N];
int f[N][N];

int main() {
    
    
  int n, m;
  cin >> n >> m >> a + 1 >> b + 1;
    
  for (int i = 1; i <= n; i++) 
    for (int j = 1; j <= m; j++) 
      if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1;
      else  f[i][j] = max(f[i - 1][j], f[i][j - 1]);
  
  cout << f[n][m];
    
  return 0;
}

Guess you like

Origin blog.csdn.net/HangHug_L/article/details/114409928