最大连续子序列和,乘积,最长递增子串,最长公共子串,子序列等问题(动态规划等)

本文地址: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;
    }
};

最长递增子序列

题目地址
http://www.nowcoder.com/practice/585d46a1447b4064b749f08c2ab9ce66?tpId=49&tqId=29347&rp=4&ru=/ta/2016test&qru=/ta/2016test/question-ranking

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

最长公共子串

牛客网题目地址
http://www.nowcoder.com/practice/02e7cc263f8a49e8b1e1dc9c116f7602?tpId=49&tqId=29349&rp=4&ru=/ta/2016test&qru=/ta/2016test/question-ranking

动态规划求解代码:

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;
    }
};

最长公共子序列

题目地址
http://www.nowcoder.com/practice/c996bbb77dd447d681ec6907ccfb488a?tpId=49&tqId=29348&rp=4&ru=/ta/2016test&qru=/ta/2016test/question-ranking

动态规划求解过程

这里写图片描述

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;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值