算法练习-动态规划

一、最长回文子串 

        对于长度为n的一个字符串A(仅包含数字,大小写英文字母),请设计一个高效算法,计算其中最长回文子串的长度

class Solution {
public:
    // abcbc
    int getLongestPalindrome(string A) {
       int len = A.size();
       vector> dp(len,vector(len,false));
       int res = 0;
       for(int i = len -1; i >= 0; i--) {
        for(int j = i; j < len;j++) {
            if(A[i] == A[j]) {
                if(j-i <= 1) { //表示相邻的位置情况
                    dp[i][j] = true;
                    res = max(res,j-i+1);
                }
                else if(dp[i+1][j-1]) {
                    dp[i][j] = true;
                    res = max(res,j-i+1); //获取最大长度
                }
            }
        }
       }
       return res;
    }
};

二、最长公共子串

        给定两个字符串str1和str2,输出两个字符串的最长公共子串,保证str1和str2的最长公共子串存在且唯一


class Solution {
  public:
    string LCS(string str1, string str2) {
        int len1 = str1.size();
        int len2 = str2.size();
        vector> dp(len1 + 1, vector(len2 + 1, 0));
        int end = 0;
        int maxLen = 0;
        for (int i = 1; i < len1 + 1; i++) {
            for (int j = 1; j < len2 + 1; j++) {
                if (str1[i - 1] == str2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1; //公共子串需要保证连续的字符相等
                } else {
                    dp[i][j] = 0;
                }
                if (maxLen < dp[i][j]) {
                    maxLen = dp[i][j];
                    end = i - 1; //记录位置信息
                }
            }
        }
        return str1.substr(end - maxLen + 1, maxLen);
    }
};

需要注意的是,公共子串和子序列的不一样的地方,子串需要保证的是连续的字符都是需要相同,所以导致动态转移方程中  dp[i][j] = dp[i-1][j-1] + 1,而不是 需要和dp[i-1][j]、dp[i][j-1] 进行比较

三、最长公共子序列

        给定两个字符串str1和str2,输出两个字符串的最长公共子串,返回最长公共子序列的长度数值

        

class Solution {
public:
    int LCS(string s1, string s2) {
        int len1 = s1.size();
        int len2 = s2.size();
        vector> dp(len1 + 1,vector(len2+1,0));
        for(int i = 1; i < len1 + 1; i++) {
            for(int j = 1; j < len2 + 1;j++) {
                if(s1[i-1] == s2[j-1]) { //下标从0开始,所以是i-1,j-1
                    dp[i][j] = dp[i-1][j-1] + 1; //s1 == s2的时候,根据状态转移方程
                }
                else {
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);//取其中一个最大的数值
                }
            }
        }
        return dp[len1][len2];
    }
};

        特别需要注意的是dp[i][j] = dp[i-1][j-1] + 1的理解,对这个需要分析和理解递归的子逻辑

四、编辑距离 

        给定两个字符串 str1 和 str2 ,请你算出将 str1 转为 str2 的最少操作数

        你可以对字符串进行3种操作:1.插入一个字 2.删除一个字符 3.修改一个字符

        

class Solution {
public:
    int editDistance(string str1, string str2) {
       int len1 = str1.size();
       int len2 = str2.size();
       vector> dp(len1+1,vector(len2+2,0));

       //dp[i][j] 表示str1的前 i 个字符 和str2的前j个字符的编辑距离的操作数
       //边界问题处理,str2 为空的时候 dp[i][0],表示str1字符串变成空字符串的操作
       for(int i = 1; i < len1+1; i++) {
            dp[i][0] = dp[i-1][0] + 1;
        }
        // 表示一个str1为空字符串,变成str2的操作数
       for(int j = 1; j< len2 + 1; j++) {
            dp[0][j] = dp[0][j-1] + 1; //表示前一个字符 直接 +1
        }
       for(int i = 1; i < len1 + 1; i++) {
        for(int j = 1; j< len2 + 1; j++) {
            if(str1[i-1] == str2[j-1]) { //当前字符一样,不需要任何操作
                dp[i][j] = dp[i-1][j-1];
            }
            else {   //当前字符不一样,需要delete or add or replace
                    // str1[i] != str2[j] 有下面操作
                    /*
                        dp[i-1][j-1] str1 or str2 替换操作 得到dp[i][j] -> dp[i-1][j-1] +1
                        dp[i-1][j] 表示str1删除i字符  + 1
                        dp[i][j-1] 表示str2 删除j字符  +1
                    */
                dp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1])) + 1;
            }
        }
       }
       return dp[len1][len2]; 
    }
};

五、连续子数组的最大和

        输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值 

class Solution {
public:
    int FindGreatestSumOfSubArray(vector array) {
        if(array.empty()) return 0;
        vector dp(array.size(),0);
        dp[0] = array[0];
        int result = dp[0];
        for(int i = 1; i < array.size(); i++) {
            dp[i] = max(dp[i-1] + array[i],array[i]);
            if(dp[i] > result) result = dp[i];
        }
        return result;
    }
};

六、兑换零钱(一)

        

给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。

如果无解,请返回-1.

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 最少货币数
     * @param arr int整型vector the array
     * @param aim int整型 the target
     * @return int整型
     */
    int minMoney(vector& arr, int aim) {
        //dp数组记录货币数值为aim的数据需要最少的货币个数
        vector dp(aim+1,aim+1);//数组初始化,每一个元素max
        dp[0] = 0; //第一个元素为0
        for(int i = 0; i < arr.size(); i++) {
            int value = arr[i]; //取出其中一个货币
            for(int j = value; j <= aim; j++) {
                dp[j] = min(dp[j],dp[j - value] + 1); //dp[v1] dp[v2]最小数
            }
        }
        if(dp[aim] == aim + 1) return -1;
        return dp[aim];
    }
};

你可能感兴趣的:(算法练习,动态规划,算法)