首页 > 其他 > 详细

阿里云天池周赛

时间:2020-09-26 21:53:38      阅读:56      评论:0      收藏:0      [点我收藏+]

??题目链接:Click me.

好多其实都是 Leetcode 的原题 0.0

K步编辑

Leetcode 原题:72. 编辑距离

编辑距离

先解决 leetcode-72 。

给定字符串 A 和 B,我们一共有 6 种不同的操作(A 和 B 各有三种)。但是下面 3 种情况是等价的:

  • A[i] 修改和对 B[i] 修改。例如 A = "cat", B = "bat" ,那么修改单词 A 的第一个字母 b -> c,和修改单词 B 的第一个字母 c -> b 是等价的。
  • 对 B[i] 插入字符和删除 A[i]
  • 对 A[i] 插入字符和删除 B[i]

那么实质上有效操作只有三种:

  1. 修改 A 的一个字符。
  2. 向 A 插入。
  3. 向 B 插入。

状态方程:dp[i, j]s1 的前 i 个字符与 s2 的前 j 个字符的编辑距离。

转移方程

if s1[i-1] == s2[j-1] then:
    dp[i][j] = min(dp[i-1][j-1], dp[i-1][j]+1, di[i][j-1]+1)
else 
    dp[i][j] = min(dp[i-1][j-1]+1, dp[i-1][j]+1, dp[i][j-1]+1)
/**
 *   dp[i-1][j]+1 表示向 A 插入一个字符后,与 B 相同
 *   dp[i][j-1]+1 表示向 B 插入一个字符后,与 A 相同
 *   dp[i-1][j-1]+1 表示修改 A 的一个字符后,与 B 相同
 **/

代码实现

含空间优化。

class Solution
{
public:
    int minDistance(string word1, string word2)
    {
        return dp1(word1, word2);
    }

    int dp1(string &s1, string &s2)
    {
        int len1 = s1.length(), len2 = s2.length();
        if (len1 == 0) return len2;
        if (len2 == 0) return len1;
        vector<int> dp(len2 + 1, 0);
        for (int j = 1; j <= len2; j++) dp[j] = j;
        vector<int> buf(dp);
        for (int i = 1; i <= len1; i++)
        {
            buf[0] = i - 1;
            dp[0] = i;
            for (int j = 1; j <= len2; j++)
            {
                if (s1[i - 1] == s2[j - 1])
                    dp[j] = min(buf[j - 1], min(buf[j], dp[j - 1]) + 1);
                else
                    dp[j] = min(buf[j - 1], min(buf[j], dp[j - 1])) + 1;
            }
            buf = dp;
        }
        return dp.back();
    }

    int dp2(const string &s1, const string &s2)
    {
        int len1 = s1.length(), len2 = s2.length();
        if (len1 == 0) return len2;
        if (len2 == 0) return len1;
        vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));
        int i = 0, j = 0;
        for (auto &v : dp) v[0] = i++;
        for (int &x : dp[0]) x = j++;
        for (i = 1; i <= len1; i++)
        {
            for (j = 1; j <= len2; j++)
            {
                if (s1[i - 1] == s2[j - 1])
                    dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
                else
                    dp[i][j] = min(dp[i - 1][j - 1], min(dp[i][j - 1], dp[i - 1][j])) + 1;
            }
        }
        return dp[len1][len2];
    }
};

本题

然后本题就很简单了:

vector<string> kDistance(vector<string> &words, string &target, int k)
{
    // write your code here
    vector<string> ans;
    for (auto &x : words)
    {
        // dp1 是上面的函数
        if (dp1(x, target) <= k)
            ans.push_back(x);
    }
    return ans;
}

折纸

题目描述:折纸,每次都是将纸从右向左对折,凹痕为 0,凸痕为 1,求折 n 次后,将纸展开所得折痕组成的 01 序列。\(1 \le n \le 20\) .

样例:

样例 1
输入: 1
输出: "0"

样例 2
输入: 2
输出: "001"

这题是找规律。当场用纸模拟一下:

1 => 0
2 => 0 0 1
3 => 001 0 011
4 => 0010011 0 0011011

我们记 \(S_n\) 是第 \(n\) 次折纸的 01 序列。

那么:

\[S_{n+1} = S_n + 0 + reverse(not(S_n)) \]

其中 \(not(x)\) 表示对 01 序列按位取反,\(reverse(x)\) 表示对字符串 \(x\) 反转。

代码:

class Solution
{
public:
    /**
     * @param n: The folding times
     * @return: the 01 string
     */
    string getString(int n)
    {
        // Write your code here
        string s = "0";
        for (int i = 1; i < n; i++)
            s = s + "0" + rev(s);
        return s;
    }

    string rev(const string &s)
    {
        string res = "";
        for (int i = (int)s.length() - 1; i >= 0; i--)
            res += (s[i] == ‘0‘) ? ‘1‘ : ‘0‘;
        return res;
    }
};

字符串的不同序列

描述:给出一个字符串,找到它的所有排列,注意同一个字符串不要打印两次。 请以字典序从小到大输出。 \(0 \le n \le 20\) .

样例

样例 1
输入:"abb"
输出:["abb", "bab", "bba"]
样例 2
输入:"aabb"
输出:["aabb", "abab", "baba", "bbaa", "abba", "baab"]

代码

class Solution
{
public:
    /**
     * @param str: A string
     * @return: all permutations
     */
    vector<string> stringPermutation(string &str)
    {
        // write your code here
        sort(str.begin(), str.end());
        vector<string> ans;
        ans.push_back(str);
        while (next_permutation(str.begin(), str.end()))
        {
            ans.push_back(str);
        }
        return ans;
    }
};

硬币排成线

Leetcode 原题:486. 预测赢家

递归解法

递归模拟 Alex 先手的 4 种情况:

  • Alex 取头,然后 Bob 取头
  • Alex 取头,然后 Bob 取尾
  • Alex 取尾,然后 Bob 取头
  • Alex 取尾,然后 Bob 取尾
class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        return rec(nums);
    }
    bool rec(vector<int> &v)
    {
        if (v.size() <= 1) return true;
        int sum = 0;
        for (int x: v)  sum+=x;
        int alex = f(v, 0, (int)v.size()-1);
        return alex >= (sum - alex);
    }
    int f(vector<int> &v, int l, int r)
    {
        if (l == r) return v[l];
        if (l+1 == r) return max(v[l], v[r]);
        if (l < r)
        {
            // Alex get 1st
            // int a = v[l] + f(v, l+2, r);
            // int b = v[l] + f(v, l+1, r-1);
            // Alex get the last
            // int c = v[r] + f(v, l+1, r-1);
            // int d = v[r] + f(v, l, r-2);
            // 为什么是 min ?因为「最优策略」下,要让对手 Bob 的得分最小
            return max(v[l] + min(f(v, l+2, r), f(v, l+1, r-1)), 
                       v[r] + min(f(v, l+1, r-1), f(v, l, r-2)));
        }
        return -1;
    }
};

动态规划

参考官方题解

状态方程:dp[i,j] 表示当数组剩下的部分为下标 i 到下标 j 时,当前玩家与另一个玩家的分数之差的最大值,注意当前玩家不一定是先手。

只有当 \(i \le j\) 时,数组剩下的部分才有意义。

\(i \le j\) 时,当前玩家可以选择 \(nusm[i]\)\(nums[j]\),然后轮到另一个玩家在数组剩下的部分选取数字。在两种方案中,当前玩家会选择最优的方案,使得自己的分数最大化。因此可以得到如下状态转移方程:

\[dp[i,j] = max(nums[i]-dp[i+1,j], nums[j]-dp[i,j-1]) \]

最后判断 \(dp[0, len-1]\) 的值,如果大于或等于 0,则先手得分大于或等于后手得分,因此先手成为赢家,否则后手成为赢家。

代码实现:

bool dpSolution(vector<int> &v)
{
    if (v.size() <= 1)
        return true;
    int len = v.size();
    vector<vector<int>> dp(len, vector<int>(len, 0));
    for (int i = 0; i < len; i++)
        dp[i][i] = v[i];
    for (int d = 1; d < len; d++)
    {
        for (int i = 0; i < (len - d); i++)
        {
            int j = i + d;
            dp[i][j] = max(v[i] - dp[i + 1][j], v[j] - dp[i][j - 1]);
        }
    }
    return dp[0][len - 1] >= 0;
}

阿里云天池周赛

原文:https://www.cnblogs.com/sinkinben/p/13736594.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!