dp basics - some summaries about LIS LCS LCIS

LIS: (longest increasing subsequence)


dp idea: dp[i] is the longest increasing subsequence length from 0 to the i-th item.

dp[i] = max(dp[j])+1    a[j] < a[i]

dp[0] = a[0];
for( int i = 1 ; i < n ; i++ ){
	dp[i] = 1;
	for( int j = 0 ; j < i ; j++ ){
		if( a[i] > a[j] ){
			dp[i] = max( dp[i] , dp[j] + 1 );
		}
	}
}

Binary optimization:

if( a[i] > dp[tot1] ){
	dp[++tot1] = a[i];
}else{
	int pos = lower_bound( dp , dp + tot1 , a[i] ) - dp;
	dp[pos] = a[i];
}


Note: dp[i] holds the minimum end of the longest increasing subsequence of length i. The entire array does not store the longest increasing subsequence.


If you want to output the longest increasing subsequence, you can use the first writing method without binary optimization, adding an array that records the previous node.

Here's a sample program: (the output needs to be backtraced)

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e5+6;
int dp[AX];
int a[AX];
int pre[AX];
void print( int x ){
	if( pre[x] != -1 ){
		print( pre[x] );
	}
	cout << a[x] << endl;
}
int main(){
	int n;
	cin >> n ;
	for( int i = 0 ; i < n ; i++ ){
		cin >> a[i];
	}
	dp[0] = 1;
	int tot = 0;
	int index ;
	memset( pre , -1 , sizeof(pre) ) ;
	for( int i = 1 ; i < n ; i++ ){
		dp[i] = 1;
		for( int j = 0 ; j < i ; j++ ){
			if( a[i] > a[j] ){
				if( dp[i] < dp[j] + 1 ){
					dp[i] = dp[j] + 1;
					pre[i] = j; //i is placed after j to form an increasing sequence
				}
			}
		}
		if( tot < dp[i] ){
			tot = dp [i];
			index = i;
		}
	}
	cout << tot << endl;
	print(index);
	return 0 ;
}


LCS: Longest Common Subsequence

dp[i][j] represents the length of the longest common subsequence obtained by matching the first i items of the a string with the first j items of the b string.


dp[i][j] = dp[i-1][j-1] + 1   ;  a[i] == b[j]

dp[i][j] = max(dp[i-1][j] , dp[i][j-1])   ; a[i] != b[j]

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e3+6;
int dp[AX][AX];
char res[AX];
int main(){
	string a , b;
	cin >> a >> b;
	int la = a.size();
	int lb = b.size();
	for( int i = 1 ; i <= la ; i ++ ){
		for( int j = 1 ; j <= lb ; j++ ){
			if( a[i-1] == b[j-1] ){
				dp[i][j] = dp[i-1][j-1] + 1;
			}else dp[i][j] = max( dp[i-1][j] , dp[i][j-1] );
		}
	}
	cout << dp[la][lb] << endl;
	int i = la, j = lb;
	int tot = 0;
	while( i >= 1 && j >= 1 ){
		if( a[i-1] == b[j-1] ){
			res [tot ++] = a [i-1];
			i -- ;
			j -- ;
		}else{
			if( dp[i-1][j] == dp[i][j] ){
				i--;
			}else{
				j--;
			}
		}
	}
	for( int k = tot - 1 ; k >= 0 ; k-- ){
		cout << res[k] ;
	}cout << endl;
	return 0 ;
}

Scrolling arrays optimize space:

	for( int i = 1 ; i <= la ; i ++ ){
		for( int j = 1 ; j <= lb ; j++ ){
			if( a[i-1] == b[j-1] ){
				dp[i%2][j] = dp[(i-1)%2][j-1] + 1;
			}else dp[i%2][j] = max( dp[(i-1)%2][j] , dp[i%2][j-1] );
		}
	}


LCIS ​​: Longest Common Ascending Subsequence

dp[i][j] represents the length of the LCIS ending with the first i term of the a sequence, the first j term of the b sequence, and ending with b[j].

If a[i] ! = b[j] then only numbers before a[i] can be paired with b[j].

If a[i] == b[j] , then dp[i][j] is the length of the longest sequence before +1

 Therefore, the transfer equation can be obtained:

dp[i][j] = dp[i-1][j] ;   a[i] != b[j]

dp[i][j] = max( dp[i-1][k] ) + 1    a[i] == b[j]    (1 <= k <= j - 1 && b[j] > b[k])

The most important thing about this algorithm is that if we follow a reasonable recursive order, the value of max(F[i-1][k]) can be updated by maintaining a max variable when accessing F[i][k] before. get. How to get it? The order of first recursion must be such that the first dimension of the state loops in the outer layer, and the second dimension loops in the inner layer. That is, after calculating F[1][len(b)], go to calculate F[2][1]. If we follow this recursive sequence, we can add a max variable to 0 at the beginning of each outer loop, and then start the inner loop. When a[i]>b[j], let max=F[i-1][j]. If the loop reaches a[i]==b[j], then let F[i][j]=max+1.

		for( int i = 1 ; i <= n ; i++ ){
			int tmp = 0;
			for( int j = 1 ; j <= m ; j++ ){
				dp[i][j] = dp[i-1][j];
				if( a[i-1] > b[j-1] ){
					tmp = max( tmp , dp[i-1][j] );
				}
				if( a[i-1] == b[j-1] ){
					dp[i][j] = tmp + 1;
				}
			}
		}
		int res = 0;
		for( int i = 1 ; i <= m ; i++ ){
			res = max( res , dp[n][i] );
		}

So how to output LCIS sequence? In fact, it is similar to outputting the longest increasing subsequence:

First add a two-dimensional array of pre[][], (the longest increasing subsequence can be set to a one-dimensional array), and record who the current state is connected to.

Every time you get a, b equal, record the previous state.


	for( int i = 1 ; i <= n ; i++ ){
		int index = -1 ;
		int tmp = 0;
		for( int j = 1 ; j <= m ; j++ ){
			dp[i][j] = dp[i-1][j];
			if( a[i-1] > b[j-1] ){
					/*tmp = max( tmp , dp[i-1][j] );*/
				if( tmp < dp[i-1][j] ){
					index = j - 1 ;
					tmp = dp[i-1][j];
				}
			}
			if( a[i-1] == b[j-1] ){
				dp[i][j] = tmp + 1;
				pre[i-1][j-1] = index;
			}
		}
	}

output:
First find the point p that matches at the end, and then get the whole sequence.
int p;
	for( int i = 1 ; i <= m ; i++ ){
		/*res = max( res , dp[n][i] );*/
		if( res < dp[n][i] ){
			res = dp[n][i];
			p = i-1;
		}
	}
	for( int i = n - 1 , j = p , k = res ; k > 0 ; i-- ){
		if( pre[i][j] != -1 ) {
			re[--k] = b[j];
			j = for [i] [j];
		}
	}
	cout << res << endl;
	for( int i = 0 ; i < res ; i++ ){
		cout << re[i] <<  ' ' ;
	}


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325765855&siteId=291194637