本文地址:http://blog.csdn.net/qq_26437925/article/details/52679690
对标题问题的整理(题目来源牛客网,leetcode, PAT),可以学习这一类型的编程思路,联想编程,不至于太混乱。
主要是动态规划算法,还有其优化过程
最大连续子序列和问题
53 . Maximum Subarray(leetcode)
题目地址
https://leetcode.com/problems/maximum-subarray/
ac代码(简单的动态规划)
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int len = nums.size();
if (len == 0)
return 0;
if (len == 1)
return nums[0];
vector<int> dp(len);
dp[0] = nums[0];
int ans = nums[0];
for (int i = 1; i < len; i++)
{
if (dp[i - 1] > 0){
dp[i] = dp[i - 1] + nums[i];
}
else{
dp[i] = nums[i];
}
if (dp[i] > ans)
ans = dp[i];
}
return ans;
}
};
1007. Maximum Subsequence Sum
在上面基础上,多求两个变量(头尾位置),仍然是简单的动态规划
参考:
http://blog.csdn.net/qq_26437925/article/details/47621173
最大连续子序列乘积问题
152. Maximum Product Subarray
题目地址
https://leetcode.com/problems/maximum-product-subarray/
乘积需要考虑正负情况
ac代码如下:
class Solution {
public:
int maxProduct(vector<int>& nums) {
int len = nums.size();
if (len == 0)
return 0;
if (len == 1)
return nums[0];
vector<int> dpMax(len);
vector<int> dpMin(len);
dpMax[0] = nums[0];
dpMin[0] = nums[0];
int ans = nums[0];
for (int i = 1; i < len; i++){
int tmp1 = dpMin[i - 1] * nums[i];
int tmp2 = dpMax[i - 1] * nums[i];
dpMin[i] = min(tmp1, tmp2);
dpMin[i] = min(dpMin[i], nums[i]);
dpMax[i] = max(tmp1, tmp2);
dpMax[i] = max(dpMax[i], nums[i]);
if (dpMax[i] > ans)
ans = dpMax[i];
}
return ans;
}
};
238. Product of Array Except Self
题目地址
https://leetcode.com/problems/product-of-array-except-self/
这题跟最大连续乘积没有关系,就是一个求解思路问题
ac代码:
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> ans;
int len = nums.size();
vector<int> left(len); // 除i之外的左边乘积
vector<int> right(len); // 除i之外的右边乘积
left[0] = 1;
left[1] = nums[0];
for (int i = 2; i < len; i++){
left[i] = left[i - 1] * nums[i - 1];
}
right[len - 1] = 1;
right[len - 2] = nums[len - 1];
for (int i = len - 3; i >= 0; i--){
right[i] = nums[i + 1] * right[i + 1];
}
for (int i = 0; i < len; i++){
int vi = left[i] * right[i];
ans.push_back(vi);
}
return ans;
}
};
最长递增子序列
O(n^2)时间复杂度,当然很容易求解
O(nlogn)事件复杂度,O(n)空间复杂度的ac代码如下
理解h数组的函数
class AscentSequence {
public:
int findLongest(vector<int> A, int n) {
// write code here
if(n <= 0)
return 0;
vector<int> h;
h.resize(n);
h[0] = A[0];
int hindex = 0;
for (int i = 1; i < n; i++)
{
if (A[i] > h[hindex])
{
h[++hindex] = A[i];
}
else{
int j = 0;
while (h[j] < A[i])
{
j++;
}
h[j] = A[i];
}
}
return hindex + 1;
}
};
二元组最长递增子序列
具体参考:
http://blog.csdn.net/qq_26437925/article/details/52228515
最长公共子串
动态规划求解代码:
class LongestSubstring {
public:
int findLongest(string A, int n, string B, int m) {
// write code here
if (n <= 0 || m <= 0)
return 0;
int **ma = new int *[m];
for (int i = 0; i < m; i++)
{
ma[i] = new int[n];
}
/*vector<vector<int>> ma(m);
for (int i = 0; i < m; i++)
ma[i].resize(n);*/
//memset(ma, 0, sizeof(int)*m*n);
int maxLen = 0;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (B[i] == A[j])
{
if (i == 0 || j == 0)
{
ma[i][j] = 1;
}
else
{
ma[i][j] = ma[i-1][j-1] + 1;
}
}
else{
ma[i][j] = 0;
}
if (ma[i][j] > maxLen)
maxLen = ma[i][j];
}
}
return maxLen;
}
};
优化空间复杂度后的ac代码
class LongestSubstring {
public:
int findLongest(string A, int n, string B, int m) {
// write code here
//dp[i][j] 表示 A[0-i] B[0-j] 的最长公共串的长度,dp[i][j] 与 dp[i-1][j-1] 相关
int maxLen = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
int k = i;
int l = j;
int len = 0;
while (k < n && j < m && A[k] == B[l]) // 往右下对角线下去
{
k++;
l++;
len ++;
}
if (len > maxLen)
maxLen = len;
}// for
}// for
return maxLen;
}
};
最长公共子序列
动态规划求解过程
ac代码:
class LCS {
public:
int findLCS(string A, int n, string B, int m) {
// write code here
if (n <= 0 || m <= 0)
return 0;
int **ma = new int *[m+1];
for (int i = 0; i < m+1; i++)
ma[i] = new int[n+1];
for (int i = 0; i < n + 1; i++)
{
ma[0][i] = 0;
}
for (int j = 0; j < m + 1; j++)
{
ma[j][0] = 0;
}
int maxLen = 0;
for (int i = 1; i < m+1; i++)
{
for (int j = 1; j < n+1; j++)
{
if (B[i - 1] == A[j - 1])
{
ma[i][j] = ma[i - 1][j - 1] + 1;
}
else{
ma[i][j] = max(ma[i][j - 1], ma[i - 1][j]);
}
if (ma[i][j] > maxLen)
maxLen = ma[i][j];
}
}
return maxLen;
}
};
改进的ac代码
class LCS {
public:
int findLCS(string A, int n, string B, int m) {
// write code here
if (n <= 0 || m <= 0)
return 0;
string s = A;
int sLen = n;
string t = B;
int tLen = m;
if (n > m)
{
s = B;
sLen = m;
t = A;
tLen = n;
}
vector<int> ma(sLen + 1, 0);
for (int i = 1; i < tLen + 1; i++)
{
vector<int> tmp = ma;// 临时变量存储ma
for (int j = 1; j < sLen + 1; j++)
{
if (t[i - 1] == s[j - 1])
ma[j] = tmp[j - 1] + 1; // 斜上方取旧的ma
else
ma[j] = max(ma[j - 1], tmp[j]); // 左边 取新的 ma, 上面取老的ma
}
}
return ma[sLen];
}
};
是否是子序列问题
392. Is Subsequence(leetcode)
题目地址
https://leetcode.com/problems/is-subsequence/
采用动态规划O(n*m)的时间复杂度会超时
采用两个指针遍历,ac代码如下
class Solution {
public:
bool isSubsequence(string s, string t) {
int sLen = s.size();
int tLen = t.size();
if (sLen == 0) // s为空串,返回true
return true;
if (sLen > tLen)
return false;
int i = 0;
int j = 0;
while (i < sLen)
{
if (j >= tLen)
return false;
while (j < tLen){
if (j >= tLen)
return false;
if (s[i] == t[j])
{
j++;
break;
}
j++;
}
i++;
}
return true;
}
};
300. Longest Increasing Subsequence
题目地址
https://leetcode.com/problems/longest-increasing-subsequence/
ac
(严格的O(logn),二分法) 最长递增子序列
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = nums.size();
if(len <= 1)
return len;
vector<int> h(len);
h[0] = nums[0];
int index = 0;
for(int i=1; i<len; i++)
{
if(nums[i] > h[index])
{
h[++index] = nums[i];
}else{
//
bool flag = false;
int pos = -1;
int left = 0;
int right = index;
while(left < right) //等于的情况在外面判断
{
int mid = (left + right) / 2;
if(h[mid] == nums[i])
{
pos = mid;
while(pos >= 0 && h[pos] == nums[i])
pos --;
pos ++;
flag = true;
break;
}else if(h[mid] < nums[i]){
left = mid + 1;
}else{
right = mid - 1;
}
}
if(!flag)
{
pos = right;
}
if(h[pos] < nums[i])
h[pos+1] = nums[i];
else // nums[i] <= h[pos]
h[pos] = nums[i];
}
}
return index + 1;
}
};
扩展1: 完美2017实习 输出最长递增子序列
题目描述
给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)
例如:给定一个长度为8的数组A{1,3,5,2,4,6,7,8},则其最长的单调递增子序列为{1,2,4,6,7,8},长度为6.
输入描述:
第一行包含一个整数T,代表测试数据组数。
对于每组测试数据:
N-数组的长度
a1 a2 … an (需要计算的数组)
保证:
1<=N<=3000,0<=ai<=MAX_INT.
输出描述:
对于每组数据,输出一个整数序列,代表最长递增子序列。
若有多组最长上升子序列,输出第一组。
保证:1<=T<=20,1<=N<=3000,0<=ai<=MAX_INT.
输入例子:
2
7
89 256 78 1 46 78 8
5
6 4 8 2 17
输出例子:
1 46 78
6 8 17
ac代码
#include <cstdio>
#include <memory>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <algorithm>
#include <sstream>
#include <list>
#include <stack>
#include <map>
#include <set>
using namespace std;
// freopen("in.txt", "r", stdin);
#define INF 0x7fffffff
int t;
int n;
vector<int> nums;
int main()
{
//freopen("in.txt", "r", stdin);
while(scanf("%d", &t) != EOF){
while(t--){
nums.clear();
scanf("%d", &n);
for(int i=0;i<n;i++)
{
int tmp;
scanf("%d", &tmp);
nums.push_back(tmp);
}
vector<int> len(n,1); // 从0到i位置 可以凑成的最长递增子序列长度
vector<int> pre(n);// 从0到i位置 凑成的最长递增子序列长度, i前一个数的下标
for(int i = 1; i < n ;i++)
{
// 当前数 前面位置的所有数
for(int j = 0; j < i;j++)
{
// 前面的数 比 当前的数小
if(nums[j] < nums[i])
{
// 前面的数 加上当前的数 可以凑成的最长递增子序列长度 len[j] + 1
if(len[j] + 1 > len[i]){
len[i] = len[j] + 1;
pre[i] = j;
}
}
}
}
int lastPos;
int maxLen = -1;
for(int i = 0;i < len.size(); i ++)
{
if(len[i] > maxLen) //找到第一个最长距离的
{
maxLen = len[i];
lastPos = i;
}
}
vector<int> ans; // 存储结果,是个逆序的
for(int i=0;i<maxLen;i++)
{
ans.push_back(nums[lastPos]);
lastPos = pre[lastPos];
}
// 打印结果
for(int i=maxLen - 1;i > 0;i--)
{
printf("%d ", ans[i]);
}
printf("%d\n", ans[0]);
}
}
return 0;
}