算法进阶__第7课(矩阵的最小路径和、最长递增子序列、最长公共子序列(长度+序列)、最长公共字符串(长度+子串)、最小编辑距离、回文最小分割数、有效的括号序列[1][2]、最长有效的括号数)

 

矩阵的最小路径和
【题目】 给定一个矩阵m,从左上角开始每次只能向右或者向下走,最后 到达右下角的位置,路径上所有的数字
累加起来就是路径和, 返回所有的路径中最小的路径和。
【举例】
如果给定的m如下:
1359
8134
5061
8840 路径1,3,1,0,6,1,0是所有路径中路径和最小的,所以返 回12。
【要求】
额外空间复杂度O( min {m , n})
//
//  main.cpp
//  advanced7
//
//  Created by 吴珝君 on 2019/6/5.
//  Copyright © 2019年 闲着也是贤者. All rights reserved.
//

#include 
#include 
#include 
using namespace std;
class Solution1 {
public:
    int dp[500][500];
    //每次的选择
    /**
     * @param grid: a list of lists of integers
     * @return: An integer, minimizes the sum of all numbers along its path
     */
    int minPathSum(vector> &grid) {
        // write your code here
        int row = grid.size();
        int col = grid[0].size();
        dp[0][0] = grid[0][0];
        for (int i = 1; i < col; i++) {
            dp[0][i] =dp[0][i -1] + grid[0][i];
        }
        for (int j = 1; j < row; j++) {
            dp[j][0] = dp[j -1][0] + grid[j][0];
        }
        for (int i = 1; i < row ; i++) {
            for (int j = 1; j < col; j++) {
                dp[i][j] = min(dp[i - 1][j], dp[i][ j -1]) + grid[i][j];
            }
        }
        return dp[row -1][col -1];
    }
};

class Solution2{
public:
    int dp[500];
    //每次的选择
    /**
     * @param grid: a list of lists of integers
     * @return: An integer, minimizes the sum of all numbers along its path
     */
    int minPathSum(vector> &grid)
    {
        // write your code here
    
        int row = grid.size();
        int col = grid[0].size();
        dp[0] = grid[0][0];//如果行数比较多的话
        for (int i = 1; i < col; i++) {
            dp[i] = dp[i -1] + grid[0][i];
        }
        
        for (int i = 1; i < row; i++) {
            dp[0] = dp[0] + grid[i][0];
            for (int j =1 ; j > &grid) {
        // write your code here
        int less = 0;
        int more = 0;
        int row = grid.size();
        int col = grid[0].size();
        //一直保持列比较小的状态
        bool colless = (row >= col) ? true : false;
        if (!colless)
        {
            less = row;
            more = col;
        }
        else
        {
            less = col;
            more = row;
            
            
        }
        dp = new int[less];
        fill(dp, dp+less, 0);
        dp[0] = grid[0][0];
        if (colless) {
            for (int i = 1; i < less; i++) {
                dp[i] = dp[i - 1] + grid[0][i];
            }
        }
        else
        {
            for (int i = 1; i < less; i++) {
                dp[i] = dp[i - 1] + grid[i][0];
            }
            
        }
        for (int i = 1; i < more; i++) {
            if (colless) {
                dp[0] =dp[0] + grid[i][0];
            }
            else
            {
                dp[0] = dp[0] + grid[0][i];
            }
            for (int j = 1; j < less; j++) {
                if (colless) {
                    dp[j] =min(dp[j - 1], dp[j]) + grid[i][j];
                }
                else
                {
                    dp[j] = min(dp[j -1], dp[j]) + grid[j][i];//为了维护比较小的开支 比较选择 行和列的最小值作为
                    //dp数组的大小。
                }
            }
            
        }
        return dp[less - 1];
    }
};

/*
 关于第三种方法的说明,这个方法的细节:看下面的例子
 1(***)  2  (*) 3   4   6
 1(***)  2  (*) 3   4   6
 1(***)  2   3   4   6
 1(***)  2   3   4   6
 这个例子的情况就是行少列多,这种情况下我们选择利用列来作为dp的大小,分别根据当前列前一列的值来计算当前的dp值
 初始化dp,也就是第一列的情况。之后计算 第二列。dp[0] = dp[0] + grid[0][i]; dp[i] = min(dp[i -1 ], dp[i]) + grid[j][i];
 */
int main(int argc, const char * argv[]) {
    // insert code here...
    std::cout << "Hello, World!\n";
    return 0;
}
最长递增子序列
【题目】
给定数组arr,返回arr的最长递增子序列。
【举例】 arr=[2,1,5,3,6,4,8,9,7],返回的最长递增子序列为 [1,3,4,8,9]。
【要求】 如果arr长度为N,请实现时间复杂度为O(NlogN)的方法。
class Solution {
public:
    int dp[500] ={0};
    /**
     * @param nums: An integer array
     * @return: The length of LIS (longest increasing subsequence)
     */
    /*
     最长上升子序列的定义:
     最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,
     这种子序列不一定是连续的或者唯一的。
     */
    //动态规划实现
    int longestIncreasingSubsequence(vector &nums){
        //
        if(nums.size() == 0)
        {
            return 0;
        }
    
       
        for (int i = 0; i < nums.size(); i++)
        {//考虑的是当前第几个字符
            dp[i] = 1;
            for (int j = 0; j < i; j++)//考虑的是以当前字符为结尾的字符是否满满足递增条件
            {
                if (nums[i] > nums[j] )
                {
                dp[i]  = max(dp[i],dp[j] + 1);//计算以j结尾的子序列拼接上当前字符是否比当前前i个字符组成的字符序列更长
                    
                }
                
            }
        }
        int max = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (max < dp[i]) {
                max = dp[i];
            }
        }
        // write your code here
        return max;
    }
    
    
    
    
};
最长公共子序列问题
【题目】 给定两个字符串str1和str2,返回两个字符串的最长公共子序列。 【举例】
str1="1A2C3D4B56",str2="B1D23CA45B6A"。 "123456"或者"12C4B6"都是最长公共子序列,返回哪一个都行。


//
//  lis.cpp
//  advanced7
//
//  Created by 吴珝君 on 2019/6/5.
//  Copyright © 2019年 闲着也是贤者. All rights reserved.
//

#include 
#include 
#include 
#include 
using namespace std;
class Solution {
public:
    int dp[150][150];
    /**
     * @param A: A string
     * @param B: A string
     * @return: The length of longest common subsequence of A and B
     */
    int longestCommonSubsequence(string A, string B) {
        // write your code here
        int i = 0;
        for(; i < A.size(); i++)
        {
            if(A[i] == B[0])
            {
                dp[i][0] = 1;
                break;
            }
        }
        for(; i< A.size(); i++)
        {
            dp[i][0] = 1;
        }
        for(i = 0; i < B.size(); i++)
        {
            if(B[i] ==A[0])
            {
                dp[0][i] = 1;
                break;
            }
        }
        for (; i < B.size(); i++)
        {
            /* code */
            dp[0][i] = 1;
        }
        for (int i = 1; i < A.size(); i++)
        {
            /* code */
            for (int j = 1; j < B.size(); j++)
            {
                /* code */
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                if(A[i] == B[j])
                {
                    dp[i][j] = max(dp[i][j], dp[i-1][j-1] +1);
                }
            }
        }
        return dp[A.size() - 1][B.size() - 1]  ;
    }
};
class Solution2 {
public:
    int dp[500] ={0};
    
    /**
     * @param A: A string
     * @param B: A string
     * @return: The length of longest common subsequence of A and B
     */
    int longestCommonSubsequence(string A, string B) {
        // write your code here
        //如何进一步压缩空间
        int i = 0;
        for(; i < B.size(); i++)
        {
            if(A[i] == B[0])
            {
                dp[i] = 1;
                break;
            }
        }
        for(; i< B.size(); i++)
        {
            dp[i] = 1;
        }
        for (int i = 1; i < A.size(); i++)
        {
            int  old = dp[0];//dp[0]代表原本dp[i-1][0]
            for (int j = 1; j < B.size(); j++)
            {
                int temp = dp[j];//代表 dp[i -1 ][j]
                if (A[i] == B[j]) {
                    dp[j] =  old + 1;//代表 dp[i][j]
                }
                else
                {
                    dp[j] = max(dp[j - 1],dp[j]);//代表dp[i][j -1] 以及 dp[i - 1][j]
                }
                old = temp;//dp[i - 1][j];
            }
            
        }
        return dp[B.size()];
    }
};
//
class Solution3 {
public:
    int dp[150][150];
    /**
     * @param A: A string
     * @param B: A string
     * @return: The length of longest common subsequence of A and B
     */
    int longestCommonSubsequence(string A, string B) {
        // write your code here
        int i = 0;
        for(; i < A.size(); i++)
        {
            if(A[i] == B[0])
            {
                dp[i][0] = 1;
                break;
            }
        }
        for(; i< A.size(); i++)
        {
            dp[i][0] = 1;
        }
        for(i = 0; i < B.size(); i++)
        {
            if(B[i] ==A[0])
            {
                dp[0][i] = 1;
                break;
            }
        }
        for (; i < B.size(); i++)
        {
            /* code */
            dp[0][i] = 1;
        }
        for (int i = 1; i < A.size(); i++)
        {
            /* code */
            for (int j = 1; j < B.size(); j++)
            {
                /* code */
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                if(A[i] == B[j])
                {
                    dp[i][j] = max(dp[i][j], dp[i-1][j-1] +1);
                }
            }
        }
        for (int i = 0; i < A.size(); i++) {
            for (int j = 0; j < B.size(); j++) {
                cout <<"  "<0)
        {
            //不一定选择了 dp[i][j];
            if (row> 0 && dp[row][col] == dp[row -1 ][ col]) {
                row--;
            }
            else if(col > 0 && dp[row][col] == dp[row ][col -1])
            {
                col--;
            }
            else//比当前的值大
            {
                len--;
                str =string(&A[row], &A[row] + 1) + str;
                row--;
                col--;
            }
        }//如果 m== 0 && n == 0 时候不处理的原因是因为如果到达第0个字符还没有结束的话 那么这个字符值一定相等
        //否则的话 一定会在 n >0 || m >0的时候结束
        cout << str<
最长公共子串问题
【题目】 给定两个字符串str1和str2,返回两个字符串的最长公共子串。 【举例】
str1="1AB2345CD",str2="12345EF",返回"2345"。
【要求】 如果str1长度为M,str2长度为N,实现时间复杂度为O(MN),额 外空间复杂度为O(1)的方法。

 

//
//  lcs.cpp
//  advanced7
//
//  Created by 吴珝君 on 2019/6/6.
//  Copyright © 2019年 闲着也是贤者. All rights reserved.
//

#include 
#include 
#include 
#include 
using namespace std;
class Solution1 {
public:
    int dp[300][300];
    int maxs = 0;
    /**
     * @param A: A string
     * @param B: A string
     * @return: the length of the longest common substring.
     */
    int longestCommonSubstring(string A, string B) {
        for (int i = 0; i < 300; i++)
        {
            for (int j = 0; j < 300; j++)
            {
                dp[i][j] = 0;
            }
        }
        int i ;
        for (i= 0; i < A.size(); i++) {
            if (A[i] ==B[0])
            {
                dp[i][0] = 1;
                //break;
            }
        }
        for (i = 0; i < B.size(); i++) {
            if (A[0] == B[i])
            {
                dp[0][i] = 1;
                //  break;
            }
        }//这个初始化和字符序列不一样。。。。
        
        for (int i = 1; i < A.size(); i++)
        {
            for (int j = 1 ; j < B.size(); j++)
            {
                if (A[i] == B[j])//以i.j为结尾的最长子串的长度
                {
                    dp[i][j] = dp[ i - 1][j - 1] + 1;
                    //  cout << "i  " << i <<"  j:  "<
最小编辑代价
【题目】 给定两个字符串str1和str2,再给定三个整数ic、dc和rc,分别代表插 入、删除和替换一个字符的代
价,返回将str1编辑成str2的最小代价。 【举例】
str1="abc",str2="adc",ic=5,dc=3,rc=2。 从"abc"编辑成"adc",把'b'替换成'd'是代价最小的,所以
返回2。 str1="abc",str2="adc",ic=5,dc=3,rc=100。 从"abc"编辑成"adc",先删除'b',然后插
入'd'是代价最小的,所以返 回8。
str1="abc",str2="abc",ic=5,dc=3,rc=2。 不用编辑了,本来就是一样的字符串,所以返回0。

 

//
//  mec.cpp
//  advanced7
//
//  Created by 吴珝君 on 2019/6/7.
//  Copyright © 2019年 闲着也是贤者. All rights reserved.
//
#include 
#include 
#include 
#include 
#include 
using namespace std;
class Solution {
public:
    int maxs = 65535;
    /**
     * @param word1: A string
     * @param word2: A string
     * @return: The minimum number of steps.
     */
    int minDistance(string word1, string word2) {
        // write your code here
        
        proccess(word1, word2, 0, 0, 0);
        return maxs;
    }
    void proccess(string &s1, string &s2 , int i , int j, int count )
    {
        if (i == s1.size() || j == s2.size()) {
            int t =0;
            if(i < s1.size())
            {
                t = s1.size() - i;
            }
            if(j < s2.size())
            {
                t = s2.size() - j;
            }
            
            maxs = min(count + t, maxs);
            return;
        }
        
        if (s1[i] ==s2[j])
        {
            proccess(s1, s2, i+1, j+1, count);
        }
        else
        {
            //删除i: i+1
            proccess(s1, s2,  i+1, j, count+1);
            //增加了i
            proccess(s1, s2,  i, j+1, count+1);
            //替换i
            proccess(s1, s2,  i+1, j+1, count+1);
        }
    }
};

class Solution1 {
public:
    int dp[300][300]= {0,0};
    /**
     * @param word1: A string
     * @param word2: A string
     * @return: The minimum number of steps.
     */
    int minDistance(string word1, string word2) {
        // write your code here
        
        if (word1.size() == 0) {
            return word2.size();
        }
        if (word2.size() == 0) {
            return word1.size();
        }
        
        
        
        //边界处理
        //  A        BACD  编辑距离
        for (int i = 0; i < word2.size(); i++)
        {
            if (word1[0] == word2[i])
            {
                dp[0][i] = i;
                
            }
            else if(i != 0)//
            {
                dp[0][i] = dp[0][i-1]+1;
            }
            else
            {
                dp[0][i] = 1;
            }
        }
        //  A        BACD  编辑距离
        for (int i = 0; i < word2.size(); i++)
        {
            if (word1[i] == word2[0])
            {
                dp[i][0] = i;
                
            }
            else if(i != 0)//
            {
                dp[i][0] = dp[i - 1][0]+1;
            }
            else
            {
                dp[i][0] = 1;
            }
        }
        
        for (int i = 1; i < word1.size(); i++) {
            for (int j = 1; j < word2.size(); j++) {
                if (word1[i] == word2[j]) {
                    dp[i][j] = min(dp[i -1][j] +1, min((dp[i][j - 1]) +1, dp[i -1][j -1]));
                }
                else
                    dp[i][j] = min(dp[i -1][j] +1, min((dp[i][j - 1]+1), dp[i -1][j -1] +1));
            }
        }
        
        return dp[word1.size() -1][word2.size() - 1];
    }
    
    
};
class Solution2 {
public:
    int dp[150]= {0};
    /**
     * @param word1: A string
     * @param word2: A string
     * @return: The minimum number of steps.
     */
    int minDistance(string word1, string word2) {
        // write your code here
        
        if (word1.size() == 0) {
            return word2.size();
        }
        if (word2.size() == 0) {
            return word1.size();
        }
        
        //边界处理
        //  A        BACD  编辑距离
        for (int i = 0; i < word2.size(); i++)
        {
            if (word1[0] == word2[i])
            {
                dp[i]= i;
            }
            else if(i != 0)//
            {
                dp[i] = dp[i-1]+1;
            }
            else
            {
                dp[i] = 1;
            }
        }
        
        //依赖 dp[ i - 1][j - 1] dp[i ][j - 1] dp[i - 1][j] 当我更新 dp[j -1] 的时候,会覆盖 dp[j -1]的值,这样我在摇更新dp[j]的时候没办法更新
        for (int i = 1; i < word1.size(); i++)
        {//行 列
            
            int old = dp[0];//dp[ i - 1][0];
            if (word1[i] == word2[0])
            {
                dp[0]= i;
            }
            else
            {
                dp[0] = dp[0] + 1;//dp[i][0];
            }
            
            for (int j = 1; j < word2.size(); j++)
            {//列数 表示的是列
                int temp = dp[j];
                if (word1[i] == word2[j]) {
                    dp[j] = min(old, 1+min(dp[j], dp[j - 1]));//dp[i -1 ][j] dp[i][j - 1]
                }
                else
                {
                    dp[j] = min(old + 1, 1+min(dp[j], dp[j - 1]));
                }
                old = temp;
            }
        }
        
        return dp[word2.size() -1];
    }
    
    
};
回文最小分割数
【题目】 给定两个字符串str,请把str切割,保证每一部分都是回文串,求最小的分割 数。
【举例】 str="AA12321BB",切成"AA","12321","BB",每一部分都是回文串,分出3个 部分,所以返回3
class Solution {
public:

    /**
     * @param s: A string
     * @return: An integer
     */
    int minCut(string &s) {
        // write your code here
       
       bool **p =  new bool *[s.size()];
       int *dp = new int [s.size() +1];
        for (int i = 0; i < s.size(); i++) {
            /* code */
            p[i] = new bool[s.size()];
        }
        
        for(int i = 0; i < s.size();i++)
        {
            dp[i] = 65535;//不影响最小值
            for(int j = 0; j < s.size(); j++)
            p[i][j] = false;
        }
       dp[s.size()] = -1;//处理最后一个元素
        for (int i = s.size() -1 ;i >=0 ; i--)
        {
            for (int j = i; j < s.size(); j++)
            {
                if (s[i] == s[j] &&((j -i < 2)|| p[i + 1][ j -1]) )
                {
                    p[i][j] = true;
                    dp[i] = min(dp[i], 1 + dp[j +1] );
                }
                
            }
        }
        return dp[0];
    } 
};
描述
给定一个字符串所表示的括号序列,包含以下字符: '(', ')', '{', '}', '[' and ']', 判定是否是有效的括号序列。

括号必须依照 "()" 顺序表示, "()[]{}" 是有效的括号,但 "([)]" 则是无效的括号。
class Solution {
public:
    stack stk;
    /**
     * @param s: A string
     * @return: whether the string is a valid parentheses
     */
    bool isValidParentheses(string s) {
        // write your code here
        if(s == "")
            return true;
        for (int i = 0; i < s.size(); i++)
        {
            if (s[i] == '(' || s[i] == '['||s[i] == '{')
            {
                stk.push(s[i]);
            }
            else
            {
                
                char c;
                if(!stk.empty())//
                   {
                       c = stk.top();
                       stk.pop();// 
                   }
                else
                    return false;
                
                if ((s[i] == ')' && c == '(')||(s[i] == ']' && c == '[')||(s[i] == '}' && c == '{')) 
                {
                    continue;
                }
          
                return false;
            }
            
        }
        if (stk.empty())
        {
            return true;
        }
        return false;
    }
};

 

描述
给定一个只包含三种类型字符的字符串:'(',')'和 '*', 编写一个函数来检查该字符串是否有效。 我们通过以下规则定义字符串的有效性:

1.任何左括号 '('必须有一个相应的右括号')'。
2.任何右括号 ')' 必须有一个相应的左括号'('。
3.左括号'(' 必须在相应的右括号 ')' 之前。
4.*可以被视为单个右括号')'或单个左括号'('或空字符串。
class Solution {
public:
    stack stk1;
    stack stk2;
    /**
     * @param s: the given string
     * @return: whether this string is valid
     */
    bool checkValidString(string s) {
        // Write your code here
        for (int i = 0; i < s.size(); i++) {
            /* code */
            if(s[i] == '(')
            {
                stk1.push(i);
            }
            else if(s[i] == '*')
            {
                stk2.push(i);
                
            }
            else
            {
                
                if(!stk1.empty())//因为*可以当作空格 所以要处理
                    stk1.pop();
                else if(!stk2.empty())
                    stk2.pop();
                else
                {
                    return false;
                }
            }
        }
        if (stk1.size() > stk2.size()) 
        {
            return false;
        }
        //要保证*在(后面
        while (!stk1.empty())
        {
            int i_left = stk1.top();
            int i_star = stk2.top();
            stk1.pop();
            stk2.pop();
            if(i_left>i_star)
                return false;
            
        }
        return true;
    }
    
};//注意行和列的处理
括号问题
【题目】 给定一个字符串str,判断是不是整体有效的括号字符串。 【举例】 str="()",返回true;str="(()
())",返回true;str="(())",返回true。 str="())"。返回false;str="()(",返回false;str="()a()",
返回false。 【补充题目】 给定一个括号字符串str,返回最长的有效括号子串。
【举例】 str="(()())",返回6;str="())",返回2;str="()(()()(",返回4。
class Solution {
public:
    int res = 0;
    /**
     * @param s: the given string
     * @return: whether this string is valid
     */
    int  getValidStringLength(string s) {
   
        if (s == "") {
            return 0;
        }
        // Write your code here
        int *dp = new int[s.size()];
        fill(dp, dp + s.size(), 0);
        for (int i = 1; i < s.size(); i++) {
            if (s[i] == ')') {
                int pre = i - s[i - 1] - 1;//最近能够匹配成功的前一个位置
                //s[i - 1] =0  说明 这个位置没有匹配成功 可以看这个地方是不是(
                //如果成功则向前追溯,看是否满足条件
                if (pre >=0 && s[pre] == '(') {
                    dp[i] = dp[i - 1] +2 + (pre > 0 ? dp[pre - 1 ] : 0);
                    //()(())匹配的最后的位置
                }
                res = max(res, dp[i]);
            }
        }
               return res;
    }
    
};//注意行和列的处理
字符串的交错组成
【题目】 给定三个字符串str1、str2和aim,如果aim包含且仅包含来自str1和str2的所有 字符,而且在aim中
属于str1的字符之间保持原来在str1中的顺序,属于str2的 字符之间保持原来在str2中的顺序,那么称aim是
str1和str2的交错组成。实现 一个函数,判断aim是否是str1和str2交错组成。
【举例】 str1="AB",str2="12"。那么"AB12"、"A1B2"、"A12B"、"1A2B"和"1AB2"等都 是str1和str2的交
错组成。

 

你可能感兴趣的:(算法专题)