LeetCode 第 199 场周赛详解

LeetCode 第 199 场周赛

1.重新排列字符串[原题链接]

题意:

给你一个字符串 s 和一个长度相同的整数数组 indices

请你重新排列字符串 s ,其中第 i 个字符需要移动到 indices[i] 指示的位置。

返回重新排列后的字符串。

数据范围:

s.length == indices.length == n
1 <= n <= 100
0 <= indices[i] < n
indices 的所有的值都是唯一的

思路:

简单的枚举即可

代码:
class Solution {
public:
    string restoreString(string s, vector<int>& indices) {
        int n = s.size();
        string r(s);
        
        for (int k = 0; k < n; ++k) r[indices[k]] = s[k];
        return r;
    }
};

2.灯泡开关 IV[原题链接]

题意:

房间中有 n 个灯泡,编号从 0n-1 ,自左向右排成一行。最开始的时候,所有的灯泡都是 关 着的。请你设法使得灯泡的开关状态和 target 描述的状态一致,其中 target[i] 等于 1 意味着第 i个灯泡是开着的,等于 0 意味着第 i 个灯是关着的。

有一个开关可以用于翻转灯泡的状态,翻转操作定义如下:选择当前配置下的任意一个灯泡(下标为 i )翻转下标从 in-1 的每个灯泡翻转时,如果灯泡的状态为 0 就变为 1,为 1 就变为 0 。

返回达成 target 描述的状态所需的 最少 翻转次数。

数据范围:

1 <= target.length <= 1e5
target[i] == '0' 或者 target[i] == '1'

思路:

可以想象,前面的操作会导致后面灯不断地发生变化,那么我们是不是要记录下每次操作后,后面灯地状态呢?这里其实是没必要的,而且由于数据量较大也不能AC。换个角度想,当枚举到 a[i] 时,那么 a[0], a[1] ....a[i - 1] 一定是亮着的,所以当 a[i] == a[i - 1] 那么就不用对 a[i] 操作,因为 a[i], a[i - 1] 被操作的次数是一样的,所以 a[i] 也是亮的。如果不一样则需要操作。
另外需要 state = '0'

代码:
class Solution {
public:
    int minFlips(string target) {
        int ret = 0;
        char state = '0';
        for (auto & v : target) {
            if (state != v) {
                state = v;
                ++ret;
            }
        }
        
        return ret;
    }
};

3.好叶子节点对的数量[原题链接]

题意:

给你二叉树的根节点 root 和一个整数 distance

如果二叉树中两个叶节点之间的最短路径长度小于或者等于 distance ,那它们就可以构成一组好叶子节点对 。

返回树中好叶子节点对的数量 。

数据范围:

tree 的节点数在 [1, 2^10] 范围内
每个节点的值都在 [1, 100] 之间
1 <= distance <= 10

思路:

我们可以记录对于每个节点,该节点所有叶子节点到该节点的距离,那么我们就可以计算该节点的左子树的叶节点与右子树节点组成的节点对的距离。这里思考一下是可以遍历所有节点对的。

代码:
class Solution {
public:
    
    int dist, ret;
    int countPairs(TreeNode* root, int dist) {
        this->dist = dist; this->ret = 0;
        diff(root);
        
        return ret;
    }
    
    vector<int> diff(TreeNode* root) {
        if (!root) return {};
        if (!root->left && !root->right) return {0};
     
        auto L = diff(root->left);
        auto R = diff(root->right);
        for (auto & lv : L)
            for (auto & rv : R)
                if (lv + rv + 2 <= dist) ++ret;
        vector<int> Ro;
        for (auto & v : L) Ro.push_back(v + 1);
        for (auto & v : R) Ro.push_back(v + 1);
        
        return Ro;
    }
};

Ro 表示根节点 root 的子树所有节点到 root 的距离。

4.压缩字符串 II[原题链接]

题意:

行程长度编码是一种常用的字符串压缩方法,它将连续的相同字符(重复 2 次或更多次)替换为字符和表示字符计数的数字(行程长度)。例如,用此方法压缩字符串 "aabccc" ,将 "aa" 替换为 "a2""ccc" 替换为 "c3" 。因此压缩后的字符串变为 "a2bc3"

注意,本问题中,压缩时没有在单个字符后附加计数 '1'

给你一个字符串 s 和一个整数 k 。你需要从字符串 s 中删除最多 k 个字符,以使 s 的行程长度编码长度最小。

请你返回删除最多 k 个字符后,s 行程长度编码的最小长度 。

数据范围:

1 <= s.length <= 100
0 <= k <= s.length

思路:

这里推一波B站零神的[讲解]。
这里稍微讲一下代码的思路:

  • 状态表示:dp[i][j] 表示前 i 个字符,删除 j 个字符的行程编码长度。
  • 状态转移:
    • 当删除 a[i] 时,dp[i][j] == dp[i - 1][j - 1]
    • 当保留 a[i] 时,那么 a[i] 必定是前 i 个字符串的编码的最后一段,所以我们只需要枚举这个编码 a[i] 连续的个数。dp[i][j = min {dp[r][j - DC] + SC | r = 1...i}DC表示s[r...i]中跟s[i]不同字符的数目,SC表示s[r...i]中跟s[i]相同字符的数目} 。我们的操作数需要减去与 s[r...i]中跟s[i]不同字符的数目
代码:
const int N = 105;
int dp[N][N];
class Solution {
public:
    const int INF = 0x3f3f3f3f;
    int getLengthOfOptimalCompression(string s, int k) {
        memset(dp, INF, sizeof(dp));
        int n = s.size();
        dp[0][0] = 0;
        
        for (int i = 1; i <= n; ++i)
            for (int j = 0; j <= k && j <= i; ++j) {
                if (j) dp[i][j] = dp[i - 1][j - 1];
                int same = 0, diff = 0;
                
                for (int r = i; r > 0; --r) {
                    if (s[r - 1] == s[i - 1]) same++;
                    else diff++;
                    if (diff <= j) {
                        dp[i][j] = min(dp[i][j], dp[r - 1][j - diff] + ctoi(same));
                    }
                }
            }
        
        return dp[n][k];
        
    }
    
    int ctoi(int cnt) {
        if (cnt == 1) return 1;
        else if (cnt < 10) return 2;
        else if (cnt < 100) return 3;
        else return 4;
    }
};

你可能感兴趣的:(算法竞赛,字符串,二叉树,算法,动态规划,leetcode)