LCIS 最长公共上升子序列
之前求过LIS和LCS,这次求两者的综合问题。那么就需要用到这两个问题的思想。
首先,用dp[i][j]表示str1和str2分别以i和j结尾的LCIS,那么对于str1[i] == str2[j]的时候,就要在1~j-1之间找到最优解,就是满足条件dp当中最大的一个,这个就是LIS的思想。如果不等的时候顺便更新一下小于str1[i]的dp最大的str2[j],这样的话在更新str1[i] == str2[j] 的时候就用O(1)的时间就能更新了。
当然,如果光求LCIS的长度的话,可以省略掉一维的空间,但是时间还是O(nm)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn =550; int s1[maxn], s2[maxn]; int dp[maxn]; int LCIS(int n, int m) { memset(dp, 0, sizeof(dp)); int k; for (int i = 1; i <= n; i++) { k = 0; for (int j = 1; j <= m; j++) { if (s1[i] == s2[j]) if (dp[j] <= dp[k]) dp[j] = dp[k] + 1; if (s1[i] > s2[j]) if (dp[k] < dp[j]) k = j; } } int ans = 0; for (int i = 1; i <= m; i++) ans = max(ans, dp[i]); return ans; } int main() { int T, n, m; scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &s1[i]); scanf("%d", &m); for (int i = 1; i <= m; i++) scanf("%d", &s2[i]); printf("%d\n", LCIS(n, m)); if (T) puts(""); } return 0; }
比较复杂一点的就是求路径的,就是求出这个LCIS序列
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn =550; int s1[maxn], s2[maxn]; int dp[maxn][maxn]; struct Path { int x, y; }path[maxn][maxn]; int R; int LCIS(int n, int m) { memset(dp, 0, sizeof(dp)); int k; for (int i = 1; i <= n; i++) { k = 0; int x = 0, y = 0; for (int j = 1; j <= m; j++) { dp[i][j] = dp[i - 1][j]; path[i][j].x = i - 1; path[i][j].y = j; if (s1[i] == s2[j]) if (dp[i][j] <= dp[i][k]) { dp[i][j] = dp[i][k] + 1; path[i][j].x = x; path[i][j].y = y; } if (s1[i] > s2[j]) if (dp[i][k] < dp[i][j]) { k = j; x = i - 1; y = j; } } } int ans = 0; for (int i = 1; i <= m; i++) if (ans < dp[n][i]) { R = i; ans = dp[n][i]; } return ans; } int p[maxn * 2]; void print(int n) { int cnt = 0; int tx = n; int ty = R; while (dp[tx][ty]) { int tmpx = path[tx][ty].x; int tmpy = path[tx][ty].y; if (dp[tmpx][tmpy] != dp[tx][ty]) p[cnt++] = s1[tx]; tx = tmpx; ty = tmpy; } for (int i = cnt - 1; i >= 0; i--) printf("%d ", p[i]); puts(""); } int main() { int T, n, m; scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &s1[i]); scanf("%d", &m); for (int i = 1; i <= m; i++) scanf("%d", &s2[i]); printf("%d\n", LCIS(n, m)); print(n); if (T) puts(""); } return 0; }
关于LCS的应用
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1092
给定一个字符串,让求最少添加多少个字符使得成为回文串。
思路:因为回文字符串是对称的,所以把回文字符串反过来和它本身求LCS,求出来还是它本身,所以,把原来的字符串反过来和它求LCS,那么求出来之后的长度就说明里买能已经存在的回文串长度,剩下的长度就是需要添加的长度。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1100; char s1[maxn], s2[maxn]; int dp[maxn][maxn]; int main() { while (~scanf("%s", s1 + 1)) { int len = strlen(s1 + 1); strcpy(s2 + 1, s1 + 1); reverse(s2 + 1, s2 + len + 1); memset(dp, 0, sizeof(dp)); for (int i = 1; i <= len; i++) { for (int j = 1; j <= len; j++) { if (s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1; else { dp[i][j] = max(dp[i][j], dp[i - 1][j]); dp[i][j] = max(dp[i][j], dp[i][j - 1]); } } } printf("%d\n", len - dp[len][len]); } return 0; }