leetcode解题思路分析(九十一)796 - 803 题

  1. 旋转字符串
    给定两个字符串, A 和 B。A 的旋转操作就是将 A 最左边的字符移动到最右边。 例如, 若 A = ‘abcde’,在移动一次之后结果就是’bcdea’ 。如果在若干次旋转操作之后,A 能变成B,那么返回True。

题目主要思路是,A + A若包含B则满足,不包含则没有,所以实际问题是找子串的问题,采取KMP即可

class Solution {
    
    
public:
    void getNext(string& p, vector<int>& next) {
    
    
        int pLen = p.size();
        next.resize(pLen, -1);
        int k = -1, j = 0;
        while (j < pLen - 1) {
    
    
            if (k == -1 || p[k] == p[j]) {
    
    
                k++;
                j++;
                next[j] = k;
            } else {
    
    
                k = next[k];
            }
        }
    }
    bool rotateString(string A, string B) {
    
    
        if (A.size() != B.size()) return false;
        A += A;
        int ALen = A.size();
        int BLen = B.size();
        vector<int> next;
        getNext(B, next);
        int i = 0, j = 0;
        while (i < ALen && j < BLen) {
    
    
            if (j == -1 || A[i] == B[j]) {
    
    
                i++;
                j++;
            } else {
    
    
                j = next[j];
            }
        }
        return j == BLen ? true : false;
    }
};


  1. 所有可能的路径
    给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)二维数组的第 i 个数组中的单元都表示有向图中 i 号节点所能到达的下一些节点,空就是没有下一个结点了。

深度优先求解

class Solution {
    
    
public:
    vector<vector<int>> ans;
    vector<int> stk;

    void dfs(vector<vector<int>>& graph, int x, int n) {
    
    
        if (x == n) {
    
    
            ans.push_back(stk);
            return;
        }
        for (auto& y : graph[x]) {
    
    
            stk.push_back(y);
            dfs(graph, y, n);
            stk.pop_back();
        }
    }

    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
    
    
        stk.push_back(0);
        dfs(graph, 0, graph.size() - 1);
        return ans;
    }
};


  1. 得分最高的最小轮调
    给定一个数组 A,我们可以将它按一个非负整数 K 进行轮调,这样可以使数组变为 A[K], A[K+1], A{K+2], … A[A.length - 1], A[0], A[1], …, A[K-1] 的形式。此后,任何值小于或等于其索引的项都可以记作一分。
    例如,如果数组为 [2, 4, 1, 3, 0],我们按 K = 2 进行轮调后,它将变成 [1, 3, 0, 2, 4]。这将记作 3 分,因为 1 > 0 [no points], 3 > 1 [no points], 0 <= 2 [one point], 2 <= 3 [one point], 4 <= 4 [one point]。
    在所有可能的轮调中,返回我们所能得到的最高分数对应的轮调索引 K。如果有多个答案,返回满足条件的最小的索引 K

差分解法
对于num,它位于 [num, n-1]里是能得分的, 基于现在数值,我们就能知道它的有效区间是什么
开始的点+1,结束的点-1
范围是 [i+1, n+i+1-nums[i]]: 这里要注意范围溢出,所以会用n来取模
计算出差分数组后,单次遍历不断累加取最大结果就是答案



class Solution {
    
    
public:
    int bestRotation(vector<int>& nums) {
    
    
        int n = nums.size();
        int diff[n];
        memset(diff, 0, sizeof(diff));
        for (int i = 0; i < n; ++i)
        {
    
    
            ++diff[(i+1)%n];
            --diff[(n+i+1-nums[i])%n];
        }

        int res = 0;
        int currScore = 0;
        int maxScore = INT_MIN;
        for (int i = 0; i < n; ++i)
        {
    
    
            currScore += diff[i];
            if (currScore > maxScore)
            {
    
    
                maxScore = currScore;
                res = i;
            }
        }

        return res;
    }
};


  1. 香槟塔
    现在当倾倒了非负整数杯香槟后,返回第 i 行 j 个玻璃杯所盛放的香槟占玻璃杯容积的比例(i 和 j都从0开始)。

逐行遍历判断即可

class Solution {
    
    
public:
    double champagneTower(int poured, int query_row, int query_glass) {
    
    
        double glasses[10000] = {
    
    0};
        glasses[0] = poured;
        int cur_row = 0;
        while(cur_row <= query_row){
    
    
            //溢出到下一行
            for(int i = cur_row * (cur_row + 1) / 2; i < (cur_row + 1) * (cur_row + 2) / 2; i++){
    
    
                double temp = glasses[i];
                if(temp > 1){
    
    
                    glasses[i] = 1;
                    glasses[i + cur_row + 1] += (temp - 1) / 2;
                    glasses[i + cur_row + 2] += (temp - 1) / 2;
                }
            }
            cur_row++;
        }
        return glasses[query_row * (query_row + 1) / 2 + query_glass];
    }
};


  1. 使序列递增的最小交换次数
    我们有两个长度相等且不为空的整型数组 A 和 B 。
    我们可以交换 A[i] 和 B[i] 的元素。注意这两个元素在各自的序列中应该处于相同的位置。
    在交换过一些元素之后,数组 A 和 B 都应该是严格递增的(数组严格递增的条件仅为A[0] < A[1] < A[2] < … < A[A.length - 1])。给定数组 A 和 B ,请返回使得两个数组均保持严格递增状态的最小交换次数。假设给定的输入总是有效的。

判断 A[i] 和 B[i] 是否交换时,只需要考虑它们之前的一个元素 A[i - 1] 和 B[i - 1] 是否被交换就可以了,因此可用动态规划求解



class Solution {
    
    
public:
    int minSwap(vector<int>& nums1, vector<int>& nums2) {
    
    
        int dn = 0;
        int ds = 1;

        int n = nums1.size();
        for (int i = 1; i < n; ++i)
        {
    
    
            int dn2 = INT_MAX;
            int ds2 = INT_MAX;
            if (nums1[i-1] < nums1[i] && nums2[i-1] < nums2[i])
            {
    
    
                dn2 = min(dn2, dn);
                ds2 = min(ds2, ds+1);
            }
            if (nums1[i-1] < nums2[i] && nums2[i-1] < nums1[i])
            {
    
    
                dn2 = min(dn2, ds);
                ds2 = min(ds2, dn+1);
            }
            dn = dn2;
            ds = ds2;
        }

        return min(dn, ds);
    }
};


  1. 找到最终的安全状态
    在有向图中,以某个节点为起始节点,从该点出发,每一步沿着图中的一条有向边行走。如果到达的节点是终点(即它没有连出的有向边),则停止。
    对于一个起始节点,如果从该节点出发,无论每一步选择沿哪条有向边行走,最后必然在有限步内到达终点,则将该起始节点称作是 安全 的。
    返回一个由图中所有安全的起始节点组成的数组作为答案。答案数组中的元素应当按 升序 排列。
    该有向图有 n 个节点,按 0 到 n - 1 编号,其中 n 是 graph 的节点数。图以下述形式给出:graph[i] 是编号 j 节点的一个列表,满足 (i, j) 是图的一条有向边。

使用DFS+三色标记法求解

class Solution {
    
    
public:
    vector<int> eventualSafeNodes(vector<vector<int>> &graph) {
    
    
        int n = graph.size();
        vector<int> color(n);

        function<bool(int)> safe = [&](int x) {
    
    
            if (color[x] > 0) {
    
    
                return color[x] == 2;
            }
            color[x] = 1;
            for (int y : graph[x]) {
    
    
                if (!safe(y)) {
    
    
                    return false;
                }
            }
            color[x] = 2;
            return true;
        };

        vector<int> ans;
        for (int i = 0; i < n; ++i) {
    
    
            if (safe(i)) {
    
    
                ans.push_back(i);
            }
        }
        return ans;
    }
};


  1. 打砖块
    给你一个数组 hits ,这是需要依次消除砖块的位置。每当消除 hits[i] = (rowi, coli) 位置上的砖块时,对应位置的砖块(若存在)会消失,然后其他的砖块可能因为这一消除操作而掉落。一旦砖块掉落,它会立即从网格中消失(即,它不会落在其他稳定的砖块上)。返回一个数组 result ,其中 result[i] 表示第 i 次消除操作对应掉落的砖块数目。

并查集的使用

class UnionFind {
    
    
private:
    vector<int> f, sz;
public:
    UnionFind(int n): f(n), sz(n) {
    
    
        for (int i = 0; i < n; i++) {
    
    
            f[i] = i;
            sz[i] = 1;
        }
    }

    int find(int x) {
    
    
        if (f[x] == x) {
    
    
            return x;
        }
        int newf = find(f[x]);
        f[x] = newf;
        return f[x];
    }

    void merge(int x, int y) {
    
    
        int fx = find(x), fy = find(y);
        if (fx == fy) {
    
    
            return;
        }
        f[fx] = fy;
        sz[fy] += sz[fx];
    }

    int size(int x) {
    
    
        return sz[find(x)];
    }
};

class Solution {
    
    
public:
    vector<int> hitBricks(vector<vector<int>>& grid, vector<vector<int>>& hits) {
    
    
        int h = grid.size(), w = grid[0].size();
        
        UnionFind uf(h * w + 1);
        vector<vector<int>> status = grid;
        for (int i = 0; i < hits.size(); i++) {
    
    
            status[hits[i][0]][hits[i][1]] = 0;
        }
        for (int i = 0; i < h; i++) {
    
    
            for (int j = 0; j < w; j++) {
    
    
                if (status[i][j] == 1) {
    
    
                    if (i == 0) {
    
    
                        uf.merge(h * w, i * w + j);
                    }
                    if (i > 0 && status[i - 1][j] == 1) {
    
    
                        uf.merge(i * w + j, (i - 1) * w + j);
                    }
                    if (j > 0 && status[i][j - 1] == 1) {
    
    
                        uf.merge(i * w + j, i * w + j - 1);
                    }
                }
            }
        }
        const vector<pair<int, int>> directions{
    
    {
    
    0, 1},{
    
    1, 0},{
    
    0, -1},{
    
    -1, 0}};
        vector<int> ret(hits.size(), 0);
        for (int i = hits.size() - 1; i >= 0; i--) {
    
    
            int r = hits[i][0], c = hits[i][1];
            if (grid[r][c] == 0) {
    
    
                continue;
            }
            int prev = uf.size(h * w);

            if (r == 0) {
    
    
                uf.merge(c, h * w);
            }
            for (const auto [dr, dc]: directions) {
    
    
                int nr = r + dr, nc = c + dc;
                
                if (nr >= 0 && nr < h && nc >= 0 && nc < w) {
    
    
                    if (status[nr][nc] == 1) {
    
    
                        uf.merge(r * w + c, nr * w + nc);
                    }
                }
            }
            int size = uf.size(h * w);
            ret[i] = max(0, size - prev - 1);
            status[r][c] = 1;
        }
        return ret;
    }
};


猜你喜欢

转载自blog.csdn.net/u013354486/article/details/120190141