算法 - 动态规划

1. 求选择方案的最大价值

给定两个数组,如下:

low = [2, 3, 1, 7], high = [4, 6, 5, 8], 数组长度一样

第i次只能从low[i] 和 hight[i] 里面取一个数。如果从low[i] 取,则第i+1次依然可以从low[i]取;如果第i次从hight[i]取,则第i+1次只能从low[i+1]中取,请给出一个选择方案,使得最终选择方案的和最大。

思路

从第i-1次到第i次,只有2中方案。一是第i-1次选择low的,第i次继续选择low,二是第i-2次选择high的,第i次选择high.

f(n)=\left\{\begin{matrix}max(f[n-1] + low[n], f[n-2] + high[n]) \\ f[0] = low[0] \\ f[1] = max(low[0]+low[1], high[1]) \end{matrix}\right.

解法

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int maxValue(vector<int>& low, vector<int>& high) {

    int a[3] = {0};
    if (low.size() == 1) return low[0];
    if (low.size() == 2) return max(low[0] + low[1], high[1]);

    a[0] = low[0];
    a[1] = max(low[0] + low[1], high[1]);
    for (int i = 2; i < low.size(); i++) {
        a[2] = max(a[0] + high[i], a[1] + low[i]);
        a[0] = a[1];
        a[1] = a[2];

    }
    return a[2];
}


int main() {
    vector<int> low{2, 3, 5, 1, 7};
    vector<int> high{4, 6, 8, 23, 12};
    cout << "Max Value:" << maxValue(low, high) << endl;

}

2. Edit Distance 2个字符串的最短距离

Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.

You have the following 3 operations permitted on a word:

  1. Insert a character
  2. Delete a character
  3. Replace a character

Example 1:

Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation: 
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')

Example 2:

Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation: 
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')

思路

当 word1[i] == word2[j] 时,dp[i][j] = dp[i - 1][j - 1],其他情况时,dp[i][j] 是其左,左上,上的三个值中的最小值加1,其实这里的左,上,和左上,分别对应的增加,删除,修改操作,具体可以参见解法一种的讲解部分,那么可以得到状态转移方程为:

dp[i][j]=\left\{\begin{matrix} dp[i - 1][j - 1]; if word1[i-1] ==work2[j-1]\\ min(dp[i - 1][j - 1], min(dp[i-1][j],dp[i][j-1])) + 1; else \end{matrix}\right.

解法

#include <iostream>
#include <map>
#include <list>
#include <string>
using namespace std;

int minDistance(string word1, string word2) {
    int m = word1.size(), n = word2.size();
    int dp[m+1][n+1];
    for (int i = 0; i <= m; ++i) dp[i][0] = i;
    for (int i = 0; i <= n; ++i) dp[0][i] = i;
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (word1[i - 1] == word2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
            }
        }
    }
    return dp[m][n];
}

int main() {
    string a("basdfeydfer");
    string b("dkjfsfdseder");
    int ret = minDistance(a, b);
    cout << ret << endl;

    return 1;
}

3. 求最大回文串 

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"

Output: "bab"

Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"

Output: "bb"

思路

思路一:Manacher's Algorithm 马拉车算法

由一个叫 Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这是非常了不起的。对于回文串想必大家都不陌生,就是正读反读都一样的字符串,比如 "bob", "level", "noon" 等等,那么如何在一个字符串中找出最长回文子串呢,可以以每一个字符为中心,向两边寻找回文子串,在遍历完整个数组后,就可以找到最长的回文子串。但是这个方法的时间复杂度为 O(n*n),并不是很高效,下面我们来看时间复杂度为 O(n)的马拉车算法。

由于回文串的长度可奇可偶,比如 "bob" 是奇数形式的回文,"noon" 就是偶数形式的回文,马拉车算法的第一步是预处理,做法是在每一个字符的左右都加上一个特殊字符,比如加上 '#',那么

bob    -->    #b#o#b#

noon    -->    #n#o#o#n# 

这样做的好处是不论原字符串是奇数还是偶数个,处理之后得到的字符串的个数都是奇数个,这样就不用分情况讨论了

#include <vector>
#include <iostream>
#include <string>

using namespace std;

string Manacher(string s) {
    // Insert '#'
    string t = "$#";
    for (int i = 0; i < s.size(); ++i) {
        t += s[i];
        t += "#";
    }
    // Process t
    vector<int> p(t.size(), 0);
    int mx = 0, id = 0, resLen = 0, resCenter = 0;
    for (int i = 1; i < t.size(); ++i) {
        p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
        while (t[i + p[i]] == t[i - p[i]]) ++p[i];
        if (mx < i + p[i]) {
            mx = i + p[i];
            id = i;
        }
        if (resLen < p[i]) {
            resLen = p[i];
            resCenter = i;
        }
    }
    return s.substr((resCenter - resLen) / 2, resLen - 1);
}

int main() {
    string s1 = "12212";
    cout << Manacher(s1) << endl;
    string s2 = "122122";
    cout << Manacher(s2) << endl;
    string s = "waabwswfd";
    cout << Manacher(s) << endl;
}

思路二:动态规划拆解问题,设状态dp[j][i]表示索引j到索引i的子串是否是回文串。则转移方程为:

解法

#include <iostream>
#include <cstring>
using namespace std;

string longestPalindrome(string s)
{
    const int n = s.size();
    bool dp[n][n];
    memset(dp, 0, sizeof(dp));

    int maxlen = 1;     //保存最长回文子串长度
    int start = 0;      //保存最长回文子串起点
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j <= i; ++j)
        {
            if(i - j < 2)
            {
                dp[j][i] = (s[i] == s[j]);
            }
            else
            {
                dp[j][i] = (s[i] == s[j] && dp[j + 1][i - 1]);
            }

            if(dp[j][i] && maxlen < i - j + 1)
            {
                maxlen = i - j + 1;
                start = j;
            }
        }
    }

    return s.substr(start, maxlen);
}

int main()
{
    string s;
    cout << "Input source string: ";
    cin >> s;
    cout << "The longest palindrome: " << longestPalindrome(s) << endl;
    return 0;
}

4. 最长公共连续子序列

戳我

5. 整数拆分

题目:给定一个整数n,将其拆分成至少2个正整数的和,并使这些整数的乘积最大化。

实例:输入 2 , 输出 1; 输入10, 输出 3*3*4=36

思路:dp(n) = max(dp(n-1)*1, dp(n-2)*2, ... , dp(1)*(n-1))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值