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
", 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
", 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 ? '
' : ' ');
}
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
", 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
", 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
", 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
", 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));
}