leetcode解题思路分析(九十六)832 - 838 题

  1. 翻转图像
    给定一个二进制矩阵 A,我们想先水平翻转图像,然后反转图像并返回结果。水平翻转图片就是将图片的每一行都进行翻转,即逆序。例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, 1]。反转图片的意思是图片中的 0 全部被 1 替换, 1 全部被 0 替换。例如,反转 [0, 1, 1] 的结果是 [1, 0, 0]。

在一次遍历中,即进行逆序也进行值的反转,用双指针完成任务

class Solution {
    
    
public:
    vector<vector<int>> flipAndInvertImage(vector<vector<int>>& image) {
    
    
        int n = image.size();
        for (int i = 0; i < n; i++) {
    
    
            int left = 0, right = n - 1;
            while (left < right) {
    
    
                if (image[i][left] == image[i][right]) {
    
    
                    image[i][left] ^= 1;
                    image[i][right] ^= 1;
                }
                left++;
                right--;
            }
            if (left == right) {
    
    
                image[i][left] ^= 1;
            }
        }
        return image;
    }
};


  1. 字符串中的查找与替换
    某个字符串 S 需要执行一些替换操作,用新的字母组替换原有的字母组(不一定大小相同)。

唯一的问题是Indices可能不是递增,所以先遍历一遍看看哪些需要改哪些不用改,然后依次修改即可


class Solution {
    
    
public:
    string findReplaceString(string s, vector<int>& indices, vector<string>& sources, vector<string>& targets) {
    
    
        stringstream ss;
        
        // 构建mark表示是否需要被替换
        int mark[s.size()];
        memset(mark, 0, sizeof(mark));
        for (int i = 0; i < indices.size(); ++i)
        {
    
    
            if (s.substr(indices[i], sources[i].size()) == sources[i])
            {
    
    
                mark[indices[i]] = i+1;
            }
        }

        // 默认值是0,所以必须>0才考虑
        int n = s.size();
        int i = 0;
        while (i < n)
        {
    
    
            if (mark[i] == 0)
            {
    
    
                ss << s[i];
                ++i;
            }
            else
            {
    
    
                ss << targets[mark[i]-1];
                i += sources[mark[i]-1].size();
            }
        }

        return ss.str();
    }
};


  1. 树中距离之和
    给定一个无向、连通的树。树中有 N 个标记为 0…N-1 的节点以及 N-1 条边 。第 i 条边连接节点 edges[i][0] 和 edges[i][1] 。返回一个表示节点 i 与其他所有节点距离之和的列表 ans。

重复很多相同的子情况,所以用动态规划很合理。为了减少计算量,这里有个很巧妙的设计:经过一次树形动态规划后其实我们获得了在 u 为根的树中,每个节点为根的子树的答案 dp,我们可以利用这些已有信息来优化时间复杂度。假设 u的某个后代节点为 v,如果要算 v 的答案,本来我们要以 v 为根来进行一次树形动态规划。但是利用已有的信息,我们可以考虑树的形态做一次改变,让 v 换到根的位置,u 变为其孩子节点,同时维护原有的 dp 信息。在这一次的转变中,我们观察到除了 u 和 v 的 dp 值,其他节点的 dp 值都不会改变,因此只要更新 dp[u] 和 dp[v] 的值即可。

class Solution {
    
    
public:
    vector<int> ans, sz, dp;
    vector<vector<int>> graph;

    void dfs(int u, int f) {
    
    
        sz[u] = 1;
        dp[u] = 0;
        for (auto& v: graph[u]) {
    
    
            if (v == f) {
    
    
                continue;
            }
            dfs(v, u);
            dp[u] += dp[v] + sz[v];
            sz[u] += sz[v];
        }
    }

    void dfs2(int u, int f) {
    
    
        ans[u] = dp[u];
        for (auto& v: graph[u]) {
    
    
            if (v == f) {
    
    
                continue;
            }
            int pu = dp[u], pv = dp[v];
            int su = sz[u], sv = sz[v];

            dp[u] -= dp[v] + sz[v];
            sz[u] -= sz[v];
            dp[v] += dp[u] + sz[u];
            sz[v] += sz[u];

            dfs2(v, u);

            dp[u] = pu, dp[v] = pv;
            sz[u] = su, sz[v] = sv;
        }
    }

    vector<int> sumOfDistancesInTree(int n, vector<vector<int>>& edges) {
    
    
        ans.resize(n, 0);
        sz.resize(n, 0);
        dp.resize(n, 0);
        graph.resize(n, {
    
    });
        for (auto& edge: edges) {
    
    
            int u = edge[0], v = edge[1];
            graph[u].emplace_back(v);
            graph[v].emplace_back(u);
        }
        dfs(0, -1);
        dfs2(0, -1);
        return ans;
    }
};

  1. 图像重叠
    给你两个图像 img1 和 img2 ,两个图像的大小都是 n x n ,用大小相同的二维正方形矩阵表示。(并且为二进制矩阵,只包含若干 0 和若干 1 )转换其中一个图像,向左,右,上,或下滑动任何数量的单位,并把它放在另一个图像的上面。之后,该转换的 重叠 是指两个图像都具有 1 的位置的数目。(请注意,转换 不包括 向任何方向旋转。)最大可能的重叠是多少?

计算便宜量,然后比较取最大值

class Solution {
    
    
public:
    const int N=30;
    int cnt[4000];
    int largestOverlap(vector<vector<int>>& img1, vector<vector<int>>& img2) {
    
    
        int n=img1.size();
        using pii=pair<int,int>;
        int res=0;
        vector<pii> vec;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(img2[i][j])
                    vec.push_back({
    
    i,j});    //找出img2中所有1的坐标
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(img1[i][j])
                {
    
    
                    for(auto& [row,col]:vec)   //遍历img2中所有1的坐标
                    {
    
    
                        int a=row-i+N;         //计算img1到img2的偏移量,+N防止负数
                        int b=col-j+N;
                        cnt[flat(a,b)]++;       //相同偏移量对应计数+1
                        res=max(res,cnt[flat(a,b)]);
                    }
                }
        return res;
    }

    int flat(int& row,int& col) //编码计算
    {
    
    
        return (row<<6)+col;
    }
};


  1. 矩形重叠
    矩形以列表 [x1, y1, x2, y2] 的形式表示,其中 (x1, y1) 为左下角的坐标,(x2, y2) 是右上角的坐标。矩形的上下边平行于 x 轴,左右边平行于 y 轴。如果相交的面积为 正 ,则称两矩形重叠。需要明确的是,只在角或边接触的两个矩形不构成重叠。给出两个矩形 rec1 和 rec2 。如果它们重叠,返回 true;否则,返回 false 。

两矩形相交,则长和宽的投影必相交,即x/y轴的两条线都要相交才算重叠

class Solution {
    
    
public:
    bool isRectangleOverlap(vector<int>& rec1, vector<int>& rec2) {
    
    
        return (min(rec1[2], rec2[2]) > max(rec1[0], rec2[0]) &&
                min(rec1[3], rec2[3]) > max(rec1[1], rec2[1]));
    }
};

  1. 新21点
    爱丽丝参与一个大致基于纸牌游戏 “21点” 规则的游戏,描述如下:
    爱丽丝以 0 分开始,并在她的得分少于 K 分时抽取数字。 抽取时,她从 [1, W] 的范围中随机获得一个整数作为分数进行累计,其中 W 是整数。 每次抽取都是独立的,其结果具有相同的概率。
    当爱丽丝获得不少于 K 分时,她就停止抽取数字。 爱丽丝的分数不超过 N 的概率是多少?

概率论数学题

class Solution {
    
    
public:
    double new21Game(int n, int k, int maxPts) {
    
    
        if (k == 0) {
    
    
            return 1.0;
        }
        vector<double> dp(k + maxPts);
        for (int i = k; i <= n && i < k + maxPts; i++) {
    
    
            dp[i] = 1.0;
        }
        dp[k - 1] = 1.0 * min(n - k + 1, maxPts) / maxPts;
        for (int i = k - 2; i >= 0; i--) {
    
    
            dp[i] = dp[i + 1] - (dp[i + maxPts + 1] - dp[i + 1]) / maxPts;
        }
        return dp[0];
    }
};


  1. 推多米诺
    一行中有 N 张多米诺骨牌,我们将每张多米诺骨牌垂直竖立。
    在开始时,我们同时把一些多米诺骨牌向左或向右推。
    返回表示最终状态的字符串。

L和R隔开的部分是一个个子数组,每个子数组再取中间值考虑每个成员的倒向即可


class Solution {
    
    
public:
    string pushDominoes(string dominoes) {
    
    
        int n = dominoes.size();
        int* diff = new int[n];
        memset(diff, 0, sizeof(int)*n);

        // 从左往右
        int weight = 0;
        for (int i = 0; i < n; ++i)
        {
    
    
            if (dominoes[i] == 'R')
            {
    
    
                weight = n;
            }
            else if (dominoes[i] == 'L')
            {
    
    
                weight = 0;
            }
            else
            {
    
    
                weight = max(weight-1, 0);
            }
            diff[i] = weight;
        }

        // 从右往左
        weight = 0;
        for (int i = n-1; i >= 0; --i)
        {
    
    
            if (dominoes[i] == 'L')
            {
    
    
                weight = n;
            }
            else if (dominoes[i] == 'R')
            {
    
    
                weight = 0;
            }
            else
            {
    
    
                weight = max(weight-1, 0);
            }
            diff[i] -= weight;
        }

        for (int i = 0; i < n; ++i)
        {
    
    
            dominoes[i] = diff[i] > 0 ? 'R' : (diff[i] < 0 ? 'L' : '.');
        }

        return dominoes;
    }
};

猜你喜欢

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