Fork me on GitHub

Leetcode第213周赛题解

1640. 能否连接形成数组

使用哈希表

class Solution {
public:
    bool canFormArray(vector<int>& arr, vector<vector<int>>& pieces) {
        unordered_map<int, int> dic;
        for (int i = 0; i < pieces.size(); i++) {
            dic[pieces[i][0]] = i;
        }

        for (int i = 0; i < arr.size();) {
            if (dic.find(arr[i]) == dic.end()) return false;
            auto& p = pieces[dic[arr[i]]];
            for (int j = 0; j < p.size(); j++, i++) {
                if (arr[i] != p[j]) return false;
            }
        }
        return true;
    }
};

暴力遍历方法

class Solution {
public:
    bool canFormArray(vector<int>& arr, vector<vector<int>>& pieces) {
        int i = 0;
        while(i<arr.size()){
            int j = 0;
            for(; j<pieces.size(); j++){
                if(pieces[j][0]==arr[i]){
                    int k = 0;
                    while(i<arr.size()&&k<pieces[j].size()){
                        if(pieces[j][k]!=arr[i]){
                            return false;
                        }
                        k++;
                        i++;
                    }
                    break;
                }
                if(j==pieces.size()-1){
                    return false;
                }
            }
        }
        return true;
    }
};

1641. 统计字典序元音字符串的数目

组合计数方法

思路和 1621. 大小为 K 的不重叠线段的数目 是类似的。如果对「隔板法」很熟悉的读者肯定可以很快想到下面的数学方法。

假设我们选取的 \(\text{a, e, i, o, u}\)的个数分别为 \(a, e, i, o, u\)当我们确定了选取个数时,需要将这些字符按照字典序进行排序并构成字符串,因此一种选取方法恰好对应一个满足题目要求的字符串。因此我们就需要求处满足下面要求的五元组 \((a, e, i, o, u)\)的个数:

  • \(a, e, i, o, u\) 均为非负整数;

  • \(a+e+i+o+u=n\)

我们可以想象面前放了一排 \(n\)个小球,而我们需要使用 \(4\)个隔板,将小球分成\(5\)个部分,每一部分可以为空。\(n\)个小球可以有 n+1n+1 个可以放隔板的位置(包括它们之间的 \(n-1\) 个空隙位置,以及左右两端的\(2\)个位置)。我们将这 \(n+1\)个位置规定为 \([1, n+1]\)中的整数,如果这 \(4\)个隔板放置的位置为 \(b_0, b_1, b_2, b_3\) ,那么它们需要满足:

\[1\leq b_0 \leq b_1 \leq b_2 \leq b_3 \leq n+1 \]

\(b_i' = b_i + i\),那么 \((b_0, b_1, b_2, b_3)\)与$ (b_0', b_1', b_2', b_3')$ 逐一对应,并且满足:

\[1 \leq b_0' < b_1' < b_2' < b_3' \leq n+4 \]

此时就可以使用组合求解方案数了,即在\([1,n+4]\)中选择 \(4\) 个整数,因此答案为:

\[\binom{n+4}{4} \]

递归方法

每一次枚举当前位置的可能性,当然,从上一个位置的地方开始

class Solution {
public:
    int ans = 0;
    void dfs(int now, int tot, int index)
    {
        if (index == tot) { ans ++; return;}
        for (int i = now; i < 5; i ++ )
        {
            dfs(i, tot, index + 1);
        }
    }
    int countVowelStrings(int n) {
        dfs(0, n, 0);
        return ans;
    }
};

1642. 可以到达的最远建筑

优先队列法

假设有ibricksj个梯子'ladders'

那么我们只需要维护一个优先队列,里面存着到当前位置为止的,前j大差值的建筑

class Solution {
public:
    int furthestBuilding(vector<int>& heights, int bricks, int ladders) {
        priority_queue<int, vector<int>, greater<int> > q;
        int sumH;
        for (int i = 1; i < heights.size(); i ++ )
        {
            int gapH = heights[i] - heights[i - 1];
            if (gapH > 0)
            {
                q.push(gapH);
                if (q.size() > ladders)
                {
                    sumH += q.top();
                    q.pop();
                }
                if (sumH > bricks)
                {
                    return i - 1; // 他还是当前位置,并没有到下一个i
                }
            }
        }
        return heights.size() - 1;    
    }
};

假设当前位置在(i,j),要到达$$(n,m)$$,那么方案数就是

\[\binom {n-i+m-j}{n-i} \]

如果我们往右移动,能么剩下可以选择的方案数就是

\[\binom {n-i+m-j-1}{n-i} \]

如果这些方案数是大于k的,说明往右走的方案中是包含第k个方案的,只需要i++
如果是小于k的,说明要往下走,需要j++,同时k的大小要减掉

\[k - \binom {n-i+m-i-1}{n-i} \]

重复上述步骤直到i==nj==m

class Solution {
public:
    static const int maxn = 35;
    long long C[maxn + 2][maxn + 2];  // long long + 两位
    string kthSmallestPath(vector<int>& destination, int k) {
        for (int i = 0; i <= maxn; i ++ )
        {
            // <= maxn
            C[0][i] = 0;
            C[i][0] = 1;
        }
        for (int i = 1; i <= maxn; i ++ )
        {
            for (int j = 1; j <= maxn; j ++ )
            {
                // i, j = 1开始
                C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]);
            }
        }
        int n = destination[0];
        int m = destination[1];
        string ans;
        int i = 0, j = 0; // 不能逗号赋值两个int i, j = 0
        while (i < n && j < m)
        {
            int a = C[n - i + m - j - 1][n - i];  // 组合数是对称的, 且先往右走,而且每时每刻在变
            if (a >= k)
            {
                ans += "H";
                j ++ ;
            }
            else
            {
                
                ans += "V";
                i ++;
                k -= a; // k - a
                // 是否会出现小于0的情况
            }
        }
        while (i < n) i ++, ans += "V";
        while (j < m) j ++, ans += "H";
        return ans;
    }
};
posted @ 2020-11-04 00:04  WalterJ726  阅读(99)  评论(0编辑  收藏  举报