zoukankan      html  css  js  c++  java
  • [经典] 最X(长 | 大和 | 大积)Y(子序列 | 子字符串)

    Note: 子序列,可以不连续;子字符串,必须连续。

    以下题目按在我看看来的难度从易到难排列:

    最大和子序列(Maximum sum subsequence)

    这道题纯属娱乐...应该不会有人出这种题吧。方案是贪心法,遇到正数就放入序列

    vector<int> msseq(vector<int> &num) {
        vector<int> result;
        for(int i : num) 
            if(i > 0)
                result.push_back(i);
        return result;
    }
    View Code

    最大和子字符串(Maximum sum substring)

    暴力方案TC为O(n^2)。更好的方案为,贪心法, 设i从0到n遍历,用gmax代表全局最大和,cursum代表当前子字符串的和。cursum为负,则放弃之前的子字符串;为正则继续向后加,每遇到一个正数,都更新一次gmax。TC = O(n),SC = O(1)。

    double msstr(vector<double> &nums) {
        double gmax = 0, cur = 0;
        for(int i : nums) {
            if(cur > 0)
                cur += i;
            else
                cur = i;
            gmax = max(gmax, cur);
        }
        return gmax;
    }
    View Code

    最长递增子字符串(Longest increasing substring)

    暴力方案TC为O(n^2)。更好的方案为,与最大和子字符串一样,贪心法,设i从0到n遍历,用gmax代表全局最长长度,len代表当前递增substring的长度。遇到递增,则len++;遇到非递增,则更新gmax,并重置len为1。TC = O(n),SC = O(1)

    int listr(vector<double> &nums) {
        if(nums.empty())
            return 0;
        int gmax = 1, cur = 1;
        for(int i = 1; i < nums.size(); i++) {
            if(nums[i] > nums[i - 1]) {
                cur++;
                gmax = max(gmax, cur);
            }
            else
                cur = 1;
        }
        return gmax;
    }
    View Code

    最大乘积子字符串(Maximum product subsequence)

    基本操作与最大乘积子序列差不多,不过是需要连续。用邻点动态规划

    double mpstr(vector<double> &nums) {
        double gmax, cmax, cmin, pmax, pmin;
        gmax = cmax = cmin = pmax = pmin = 1;
        for(double i : nums) {
            cmax = max(i, max(pmax * i, pmin * i));
            cmin = min(i, min(pmax * i, pmin * i));
            pmax = cmax;
            pmin = cmin;
            gmax = max(gmax, cmax);
        }
        return gmax;
    }
    View Code

    最大乘积子序列(Maximum product subsequence)

    这题与最大和子序列差不多吧...如果无负数,遇到大于1的数就放入序列;不过需要考虑正负号,用邻点动态规划

    double mpseq(vector<double> &nums) {
        double cmax, cmin, pmax, pmin;
        cmax = cmin = pmax = pmin = 1;
        for(double i : nums) {
            cmax = max(max(i, pmax), max(pmax * i, pmin * i));
            cmin = min(min(i, pmin), min(pmax * i, pmin * i));
            pmax = cmax;
            pmin = cmin;
        }
        return pmax;
    }
    View Code

    最长递增子序列(Longest increasing subsequence)

    暴力方案TC为O(2^n)。如果用贪心法,分析知道根据第i个包含的信息,无法覆盖前i个数的情况,故第i+1个数的决策没法做;更好的方案为,全局动态规划,用opt[i]记录到i为止最长的且包含 i上字符的最长递增subsequence,opt[i]初始为1,动态转移方程为opt[i] = max(opt[j] + 1, opt[i])。

    vector<int> miseq(vector<int> &num) {
        vector<int> result;
        if(num.empty())
            return num;
        vector<int> opt(num.size(), 1), record(num.size(), -1);
        for(int i = 0; i < num.size(); i++) {
            for(int j = 0; j < i; j++) {
                if(num[i] > num[j] && opt[j] + 1 > opt[i]) {
                    opt[i] = opt[j] + 1;
                    record[i] = j;
                }
            }
        }
        int last = -1, gmax = 0;
        for(int i = 0; i < num.size(); i++) {
            if(opt[i] > gmax) {
                gmax = opt[i];    
                last = i;
            }
        }
        while(last >= 0) {
            result.push_back(num[last]);
            last = record[last];
        }
        reverse(result.begin(), result.end());
        return result;
    }
    View Code

    最长匹配子字符串(Longest common substring) 

    暴力方案TC为O(m*n*max(m,n))。更好的方案为,邻点动态规划,用opt[i][j]记录到M的i - 1位置与N的j - 1位置为止,且包含i - 1,j - 1的最长匹配子字符串。将0位置设为岗哨,其中opt[0][j] = opt[i][0] = 0,动态转移方程为opt[i][j] = M[i] == N[j] ? opt[i - 1][j - 1] + 1 : 0。TC = O(m*n),SC = O(m * n),用滚动数组可以压缩到O(m) or O(n)。

    string lcstr(string M, string N) {
        //找到最长匹配子字符串的长度
        int maxlen = INT_MIN, ri = -1;
        vector<vector<int> > opt(M.size() + 1, vector<int>(N.size() + 1, 0));
        for(int i = 1; i <= M.size(); i++)
            for(int j = 1; j <= N.size(); j++) {
                opt[i][j] = M[i - 1] == N[j - 1] ? opt[i - 1][j - 1] + 1 : 0;
                if(opt[i][j] > maxlen) {
                    maxlen = opt[i][j];
                    ri = i;
                }
            }
        //将最大值对应的匹配子字符串输出
        string result = "";
        int starti = ri - maxlen;
        for(int i = starti; i < ri; i++)
            result += M[i];
        return result;
    }
    View Code

    最长匹配子序列(Longest common subsequence) 

    更好的方案为,与最长匹配子字符串类似,邻点动态规划,用opt[i][j]记录到M的i - 1位置与N的j - 1位置为止,且包含i - 1,j - 1的最长匹配子字符串。将0位置设为岗哨,其中opt[0][j] = opt[i][0] = 0,动态转移方程为opt[i][j] = M[i] == N[j] ? opt[i - 1][j - 1] + 1 : max(opt[i - 1][j], opt[i][j - 1])。TC = O(m*n),SC = O(m*n),滚动数组同样可以压缩到O(m) or O(n)。

    //只是找长度很简单...
    int lenlcstr(string M, string N) {
        //找到最长匹配子字符串的长度
        vector<vector<int> > opt(M.size() + 1, vector<int>(N.size() + 1, 0));
        for(int i = 1; i <= M.size(); i++)
            for(int j = 1; j <= N.size(); j++)
                opt[i][j] = M[i - 1] == N[j - 1] ? opt[i - 1][j - 1] + 1 : max(opt[i - 1][j], opt[i][j - 1]);
        return opt[M.size()][N.size()];
    }
    //还要返回一个最长匹配字符串
    string lcstr(string M, string N) {
        //记录路径
        vector<vector<int> > track(M.size() + 1, vector<int>(N.size() + 1, -1));
        //找到最长匹配子字符串的长度
        vector<vector<int> > opt(M.size() + 1, vector<int>(N.size() + 1, 0));
        for(int i = 1; i <= M.size(); i++)
            for(int j = 1; j <= N.size(); j++) {
                if(M[i - 1] == N[j - 1]) {
                    opt[i][j] = opt[i - 1][j - 1] + 1;
                    track[i][j] = 0;
                }
                else {
                    if(opt[i - 1][j] > opt[i][j - 1]) {
                        track[i][j] = 1;
                        opt[i][j] = opt[i - 1][j];
                    }
                    else {
                        track[i][j] = 2;
                        opt[i][j] = opt[i][j - 1];
                    }
                }
            }
        int i = int(M.size()), j = int(N.size());
        string result = "";
        while(track[i][j] > -1) {
            if(track[i][j] == 0) {
                result += M[i - 1];
                i--;
                j--;
            }
            else if(track[i][j] == 1)
                i--;
            else
                j--;
        }
        reverse(result.begin(), result.end());
        return result;
    }
    View Code

    编辑距离(Edit distance)

    更好的方案,与LCS类似,邻点动态规划

    int edist(string s1, string s2) {
        int m = int(s1.length()), n = int(s2.length());
        vector<vector<int> > opt(m + 1, vector<int> (n + 1, 0));
        for(int i = 0; i <= m; i++)
            opt[i][0] = i;
        for(int j = 0; j <= n; j++)
            opt[0][j] = j;
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= n; j++) {
                opt[i][j] = min(min(opt[i - 1][j], opt[i][j - 1]), opt[i - 1][j - 1]) + 1;
                if(s1[i - 1] == s2[j - 1])
                    opt[i][j] = min(opt[i][j], opt[i - 1][j - 1]);
            }
        return opt[m][n];
    }
    View Code

    最长无重复子字符串(Longest substring with no duplicate characters)

    用一个unordered_set<char>里存已有的char,遇到加入一个新的char,则在set里找,如果没找到,则加入set,并且count++;如果找到了,则在substring的循环弹出,更新set,并循环count--,直到弹出新加入的char的重复字符。

  • 相关阅读:
    js中undefined,null,NaN的区别
    js中数字计算精度
    BestCoder Round #32
    POJ 2299 求逆序对(归并排序或树状数组)
    POJ 2603
    CodeForces 515C
    POJ 1853 背包问题
    UVA 10115 子符串替换
    POJ 1155 树状dp
    HDU 2196 树状dp 求树中节点之间的最长距离
  • 原文地址:https://www.cnblogs.com/littletail/p/5260941.html
Copyright © 2011-2022 走看看