# Leetcode 第215场周赛题解

# Problem A - 设计有序流 (opens new window)

模拟。

  • 插入操作的均摊时间复杂度为O(1)O(1)。因为指针是单向移动的。
  • 空间复杂度O(N)O(N)
参考代码(C++)
class OrderedStream {
    int ptr = 1;
    vector<string> v;
public:
    OrderedStream(int n) {
        v = vector<string>(n + 1);
    }
    
    vector<string> insert(int id, string value) {
        v[id] = value;
        vector<string> ret;
        while (ptr < v.size() && !v[ptr].empty())
            ret.emplace_back(v[ptr++]);
        return ret;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Problem B - 确定两个字符串是否接近 (opens new window)

首先判断长度是否相等。在长度相等的情况下,题目中的两种操作可以理解为:

  1. 对字符串进行任意排列
  2. 交换两个字符的频次

所以实际上,我们需要判断的是:

  1. 两个字符串的字符集是否一致
  2. 两个字符串的字符频率集是否一致

统计频次,分别进行判断即可。

  • 时间复杂度O(l1+l2+ClogC)O(|l_1|+|l_2|+C\log C)CC为字母表的大小。
  • 空间复杂度O(C)O(C)
参考代码(C++)
class Solution {
public:
    bool closeStrings(string word1, string word2) {
        int l1 = word1.size(), l2 = word2.size();
        if (l1 != l2)
            return false;
        vector<int> v1(26), v2(26);
        for (char c : word1)
            v1[c - 'a']++;
        for (char c : word2)
            v2[c - 'a']++;
        for (int i = 0; i < 26; ++i)
            if ((v1[i] > 0) ^ (v2[i] > 0))
                return false;
        sort(v1.begin(), v1.end());
        sort(v2.begin(), v2.end());
        for (int i = 0; i < 26; ++i)
            if (v1[i] != v2[i])
                return false;
        return true;
    }
};     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# Problem C - 将 x 减到 0 的最小操作数 (opens new window)

使用双指针。初始状态左指针在前缀和大于等于xx的第一个位置处。之后右指针从最右边开始左移,左指针相应向左移动。在过程中不断更新最优解。

  • 时间复杂度O(N)O(N)
  • 空间复杂度O(1)O(1)
参考代码(C++)
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int n = nums.size();
        int sum = 0;
        int l = 0;
        while (l < n && sum < x)
            sum += nums[l++];
        if (sum < x)
            return -1;
        int ans = INT_MAX;
        if (sum == x) {
            ans = l;
            if (l == n)
                return n;
        }
        int rsum = 0;
        for (int r = n - 1; r >= 0; --r) {
            rsum += nums[r];
            while (l >= 1 && rsum + sum > x)
                sum -= nums[--l];
            if (sum + rsum == x)
                ans = min(ans, n - r + l);
        }
        return ans == INT_MAX ? -1 : ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# Problem D - 最大化网格幸福感 (opens new window)

较麻烦的三进制状态压缩动态规划。可以利用类似滑动窗口(或者叫轮廓线)的方法进一步优化时间复杂度(参见zerotrac (opens new window)newhar (opens new window)的题解)。这里仅给出基本的按行动态规划的实现。

  • 时间复杂度O(IEmax(N,M)32min(N,M))O(IE\cdot\max(N,M)\cdot3^{2\cdot\min(N,M)})
  • 空间复杂度O(K)O(K)
参考代码(C++)
int dp[26][26][243];
int three[6] = {1, 3, 9, 27, 81, 243};
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, -1, 0, 1};
int g[243][243];
int points[3] = {0, 120, 40};
int delta[3][3] = {{0, 0, 0}, {0, -60, -10}, {0, -10, 40}};

class Solution {
public:
    int getMaxGridHappiness(int m, int n, int intro, int extro) {
        if (m < n)
            swap(m, n);
        for (int i = 0; i <= intro; ++i)
            for (int j = 0; j <= extro; ++j)
                for (int k = 0; k < three[n]; ++k)
                    dp[i][j][k] = -1;
        dp[0][0][0] = 0;
        int ans = 0;
        
        auto get_points = [&](int last, int nxt) {
            int pts = 0;
            vector<vector<int>> mat(2, vector<int>(n));
            for (int j = 0; j < n; ++j) {
                mat[0][j] = last % 3;
                last /= 3;
                mat[1][j] = nxt % 3;
                nxt /= 3;
            }
            for (int j = 0; j < n; ++j) {
                pts += points[mat[1][j]];
                pts += delta[mat[0][j]][mat[1][j]];
                if (j + 1 < n)
                    pts += delta[mat[1][j]][mat[1][j + 1]];
            }
            return pts;
        };
        for (int i = 0; i < three[n]; ++i)
            for (int j = 0; j < three[n]; ++j)
                g[i][j] = get_points(i, j);
        
        vector<pair<int, int>> decode(three[n]);
        for (int i = 0; i < three[n]; ++i) {
            int d = i;
            while (d) {
                if (d % 3 == 1)
                    decode[i].first++;
                if (d % 3 == 2)
                    decode[i].second++;
                d /= 3;
            }
        }
        
        for (int i = 0; i < m; ++i) {
            for (int p = intro; p >= 0; --p)
                for (int q = extro; q >= 0; --q) {
                    for (int last = 0; last < three[n]; ++last) {
                        if (dp[p][q][last] == -1)
                            continue;
                        for (int nxt = 1; nxt < three[n]; ++nxt) {
                            auto [a, b] = decode[nxt];
                            if (p + a > intro || q + b > extro)
                                continue;
                            dp[p + a][q + b][nxt] = max(dp[p + a][q + b][nxt], dp[p][q][last] + g[last][nxt]);
                        }
                        dp[p][q][0] = max(dp[p][q][0], dp[p][q][last]);
                        if (last != 0)
                            dp[p][q][last] = -1;
                    }
                }
        }
        for (int i = 0; i <= intro; ++i)
            for (int j = 0; j <= extro; ++j)
                for (int k = 0; k < three[n]; ++k)
                    ans = max(ans, dp[i][j][k]);
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77