一、最大子段和
问题描述:给定n个整数(可能有负数)组成的序列a1,a2,…,an,求该序列的连续m个数的和的最大值,当所给的整数均为负数时和为0。
DP
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10005;
int a[maxn], dp[maxn];//dp[i]表示到第i个数的最大子段和
int main() {
int n; cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
dp[1] = a[1];
int maxx = 0;//记录最大子段和
for(int i = 1; i <= n; i++) {
dp[i] = max(a[i],/*从当前的数重新开始记录最大子段和*/ dp[i - 1] + a[i]/*加上a[i]后的最大子段和*/);
maxx = max(maxx, dp[i]);
}
if(maxx < 0) cout << 0 << "\n";
else cout << maxx << "\n";
return 0;
}
二、最长上升子串&最长上升子序列
1、子串:子串指的是字符串中连续的n个字符,如abcdefg中,ab,cde,fg等都属于它的字串。
2、子序列:子序列指的是字符串中不一定连续但先后顺序一致的n个字符,即可以去掉字符串中的部分字符,但不可改变其前后顺序。如abcdefg中,acdg,bdf属于它的子序列。
3、最长上升子串
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], dp[maxn];//dp[i]表示到第i项的最长子串长度
int main() {
int n; scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
int maxx = 0;
for(int i = 1; i <= n; i++) {
if(a[i] > a[i - 1]) dp[i] = dp[i - 1] + 1;
else dp[i] = 1;
maxx = max(maxx, dp[i]);
}
printf("%d\n", maxx);
return 0;
}
4、最长上升子序列
(1)O(n^2) DP
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], dp[maxn];//dp[i]表示前i个数以a[i]结尾的最长上升子序列长度
int main() {
int n; scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
dp[i] = 1;//每个数都是一个子序列
}
int maxx = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j < i; j++) {
if(a[i] > a[j]) dp[i] = max(dp[i], dp[j] + 1);
maxx = max(maxx, dp[i]);
}
}
printf("%d\n", maxx);
return 0;
}
(2)O(nlogn)
lower_bound(begin, end, num):从数组的begin位置到end - 1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], ans[maxn];
int main() {
int n; cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
ans[1] = a[1];
int cnt = 1;
for(int i = 1; i <= n; i++) {
if(a[i] > ans[cnt]) ans[++cnt] = a[i];
else {
int pos = lower_bound(ans + 1, ans + cnt + 1, a[i]) - ans;
ans[pos] = a[i];
}
}
for(int i = 1; i <= cnt; i++) cout << ans[i] << " ";
cout << "\n";
cout << cnt << "\n";
return 0;
}
我们以数列6 4 3 2 1 8 5 6为例:
(1)ans[1] = a[1] = 6;
(2)进入循环a[2] (4) < ans[1] (6),pos = 1,ans[1] = 4;
(3)判断a[3] (3) < ans[1] (4),pos = 1,ans[1] = 3;
(4)判断a[4] (2) < ans[1] (3),pos = 1,ans[1] = 2;
(5)判断a[5] (1) < ans[1] (2),pos = 1,ans[1] = 1;
(6)判断a[6] (8) > ans[1] (1),ans[++1] = a[6] = 8;
此时ans数组:1,8
(7)判断a[7] (5) < ans[2] (8),pos = 2,ans[2] = a[7] = 5;
(8)判断a[8] (6) > ans[2] (5),ans[++2] = a[8] = 6;
ans数组:1,5,6
三、最长公共子串&最长公共子序列
1、最长公共子串:给定两个字符串,它们同时含有的最长子串则被称为最长公共子串。
2、最长公共子序列:给定两个字符串,它们同时含有的最长子序列则被称为最长公共子序列。
3、最长公共子串
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], b[maxn], dp[maxn][maxn];//dp[i][j]表示第一个字符串第i位和第二个字符串第j位的最长公共子串
int main() {
int l1, l2; cin >> l1 >> l2;
for(int i = 1; i <= l1; i++) cin >> a[i];
for(int i = 1; i <= l2; i++) cin >> b[i];
int maxx = 0;
for(int i = 1; i <= l1; i++) {
for(int j = 1; j <= l2; j++) {
if(a[i] != b[j]) dp[i][j] = 0;
else dp[i][j] = dp[i - 1][j - 1] + 1;
maxx = max(maxx, dp[i][j]);
}
}
cout << maxx << "\n";
return 0;
}
4、最长公共子序列
#include <bits/stdc++.h>
using namespace std;
int a[10000], b[10000], dp[100][100];
int main() {
int l1, l2;
cin >> l1 >> l2;
for(int i = 1; i <= l1; i++) cin >> a[i];
for(int i = 1; i <= l2; i++) cin >> b[i];
int ans = 0;
for(int i = 1; i <= l1; i++){
for(int j = 1; j <= l2; j++){
if(i == 0 || j == 0) dp[i][j] = 0;
else {
dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
if(a[i] == b[j]) dp[i][j] = max(dp[i][j], dp[i-1][j-1] + 1);
}
ans = max(ans, dp[i][j]);
}
}
cout << ans << endl;
return 0;
}