LIS && LCS && LCIS && LPS && MCS模板
1. LIS (Longest Increasing Subsequence)
O (n^2):
/* LIS(Longest Increasing Subsequence) 最长上升子序列 O (n ^ 2) 状态转移方程:dp[i] = max (dp[j]) + 1 (a[j] < a[i],1 <= j < i) 附带有print输出路径函数 */ void LIS(void) { int ret = 0, last = 0; for (int i=1; i<=n; ++i) { dp[i] = 1; fa[i] = -1; for (int j=1; j<i; ++j) { if (a[j] < a[i] && dp[i] < dp[j] + 1) { dp[i] = dp[j] + 1; fa[i] = j; } } if (ret < dp[i]) { ret = dp[i]; last = i; } } printf ("%d\n", ret); print (last); puts (""); } void print(int x) { if (fa[x] != -1) { print (fa[x]); printf (" %d", a[x]); } else printf ("%d", a[x]); }
O (nlogn):
/* LIS 二分查找优化, O(nlogn) 设当前最长递增子序列为len,考虑元素a[i]; 若d[len]<a[i],则len++,并使d[len]=a[i]; 否则,在d[1~len]中二分查找第一个大于等于a[i]的位置j,使d[j]=a[i]。附上打印路径代码(准确性未知) */ void LIS(void) { int len = 1; d[1] = a[1]; fa[1] = -1; for (int i=2; i<=n; ++i) { if (d[len] < a[i]) { d[++len] = a[i]; pos[len] = i; fa[i] = pos[len-1]; } else { int j = lower_bound (d+1, d+1+len, a[i]) - d; d[j] = a[i]; pos[j] = i; fa[i] = (j == 1) ? -1 : pos[j-1]; } } printf ("%d\n", len); vector<int> res; int i; for (i=pos[len]; ~fa[i]; i=fa[i]) res.push_back (a[i]); res.push_back (a[i]); for (int i=res.size ()-1; i>=0; --i) printf ("%d%c", res[i], i == 0 ? '\n' : ' '); }
2. LCS (Longest Common Subsequence)
/* LCS(Longest Common Subsequence): 状态转移方程:dp[i][j] = dp[i-1][j-1] + 1; (s[i-1] == t[i-1]) dp[i][j] = max (dp[i][j-1], dp[i-1][j]);(s[i-1] != t[i-1]) 可滚动数组优化。附带有print输出路径函数。 */ void LCS(void) { memset (dp, 0, sizeof (dp)); memset (fa, 0, sizeof (fa)); for (int i=0; i<=lens; ++i) fa[i][0] = -1; for (int i=0; i<=lent; ++i) fa[0][i] = 1; for (int i=1; i<=lens; ++i) { for (int j=1; j<=lent; ++j) { if (s[i-1] == t[j-1]) { dp[i][j] = dp[i-1][j-1] + 1; fa[i][j] = 0; } else if (dp[i-1][j] >= dp[i][j-1]) { dp[i][j] = dp[i-1][j]; fa[i][j] = -1; } else { dp[i][j] = dp[i][j-1]; fa[i][j] = 1; } } } printf ("%d\n", dp[lens][lent]); print (lens, lent); puts (""); } void print(int x, int y) { if (!x && !y) return ; if (fa[x][y] == 0) { print (x-1, y-1); printf ("%c", s[x-1]); } else if (fa[x][y] == -1) { print (x-1, y); printf ("%c", s[x-1]); } else { print (x, y-1); printf ("%c", t[y-1]); } }
3. LCIS (Longest Common Increasing Subsequence)
/* LCIS(Longest Common Increasing Subsequence) 最长公共上升子序列 状态转移方程:a[i] != b[j]: dp[i][j] = dp[i-1][j]; a[i] == b[j]: dp[j]=max(dp[j],dp[k]); (1<=k<j&&b[k]<b[j]) 打印路径时按照b[i]来输出 */ void LCIS(void) { memset (dp, 0, sizeof (dp)); memset (fx, 0, sizeof (fx)); memset (fy, 0, sizeof (fy)); int sx = 0, sy = 0; int ret = 0, k = 0; for (int i=1; i<=n; ++i) { k = 0; for (int j=1; j<=m; ++j) { dp[i][j] = dp[i-1][j]; //以a[]为主循环,每个a[i],去找每个b[j] fx[i][j] = i - 1; fy[i][j] = j; if (a[i] == b[j] && dp[i][j] < dp[i][k] + 1) { //满足LCS dp[i][j] = dp[i][k] + 1; //在1~j-1找到b[k]<a[i],满足LIS,在b[k]上更新dp fx[i][j] = i; fy[i][j] = k; } else if (a[i] > b[j] && dp[i][j] > dp[i][k]) k = j; //找到最优的k if (ret < dp[i][j]) { ret = dp[i][j]; //更新所有dp中的最大值 sx = i, sy = j; } } } printf ("%d\n", ret); fir = true; print (sx, sy, -1); puts (""); } void print(int x, int y, int last) { //bool fir; if (x == 0 || y == 0) return ; print (fx[x][y], fy[x][y], y); if (y != last) { if (fir) printf ("%d", b[y]), fir = false; else printf (" %d", b[y]); } }
4. LPS (Longest Palidromic Subsequence)
/* LCS的思想,dp[i][j]表示i到j的最长回文串长度,状态转移方程: 1. dp[j][j+i-1] = dp[j+1][j+i-2] + 2; (str[j] == str[j+i-1]) 2. dp[j][j+i-1] = max (dp[j+1][j+i-1], dp[j][j+i-2]); (str[j] != str[j+i-1]) ans[1][len]是string类型,记录LPS字符 */ void LPS(void) { int len = strlen (str + 1); memset (dp, 0, sizeof (dp)); for (int i=1; i<=len; ++i) dp[i][i] = 1; for (int i=1; i<=len; ++i) ans[i][i] = str[i]; for (int i=2; i<=len; ++i) { //区间长度 for (int j=1; j+i-1<=len; ++j) { //[j, j+i-1] if (str[j] == str[j+i-1]) { if (i == 2) { dp[j][j+i-1] = 2; ans[j][j+i-1] = ans[j][j] + ans[j+i-1][j+i-1]; continue; } dp[j][j+i-1] = dp[j+1][j+i-2] + 2; ans[j][j+i-1] = str[j] + ans[j+1][j+i-2] + str[j+i-1]; } else if (dp[j+1][j+i-1] > dp[j][j+i-2]) { dp[j][j+i-1] = dp[j+1][j+i-1]; ans[j][j+i-1] = ans[j+1][j+i-1]; } else if (dp[j][j+i-2] > dp[j+1][j+i-1]) { dp[j][j+i-1] = dp[j][j+i-2]; ans[j][j+i-1] = ans[j][j+i-2]; } else { dp[j][j+i-1] = dp[j+1][j+i-1]; ans[j][j+i-1] = min (ans[j+1][j+i-1], ans[j][j+i-2]); } } } int mlen = dp[1][len]; for (int i=0; i<mlen; ++i) { printf ("%c", ans[1][len][i]); } puts (""); }
5. MCS (Maximum Continuous Subsequence)
O (n):
/* MCS (Maximum Continuous Subsequence) 最大子序列和 O (n) 1. DP 2. 前缀 若有多个答案输出第一个,均给出区间端点 */ void MCS(int n) { int l = 0, ll = 0, rr = 0; int sum = -INF, mx = -INF; for (int i=1; i<=n; ++i) { if (sum + a[i] < a[i]) { sum = a[i]; l = i; } else sum += a[i]; if (sum > mx) { mx = sum; ll = l, rr = i; } } printf ("%d %d %d\n", mx, ll, rr); }
O (n) another:
//O (n) //another void MCS(int n) { int l = 0, ll = 0, rr = 0; int sum = 0, mx = -INF, mn = 0; for (int i=1; i<=n; ++i) { sum += a[i]; if (sum - mn > mx) { mx = sum - mn; ll = l; rr = i; } if (sum < mn) { mn = sum; l = i; } } printf ("%d %d %d\n", mx, ll + 1, rr); }
O (nlogn):
//O (nlogn) //输出端点困难 int MCS(int *a, int l, int r) { if (l == r) { if (a[l] > 0) return a[l]; else return 0; } int mid = (l + r) >> 1; int lmax = MCS (a, l, mid); int rmax = MCS (a, mid + 1, r); int lbmax = 0, lbsum = 0; for (int i=mid; i>=left; --i) { //之前写错了,应该是连续的 lbsum += a[i]; if (lbmax < lbsum) lbmax = lbsum; } int rbmax = 0, rbsum = 0; for (int i=mid+1; i<=r; ++i) { rbsum += a[i]; if (rbmax < rbsum) rbmax = rbsum; } return max (lbmax + rbmax, max (lmax, rmax)); }
编译人生,运行世界!