【leetcode】算法题记录(71-80)

@TOC

简化路径

在这里插入图片描述

解题思路,看到题目要求,很容易的就想到了利用栈来解决问题,使用栈存储地址路径,然后通过遍历路径获得每一层地址的字符串,然后根据地址字符串判断操作,如果是..,则当栈不为空的情况出栈,如果是.或者空则不操作,否则入栈。

AC代码:

class Solution {
public:
    string simplifyPath(string path) {
        if(path.size() == 0) return "/";
        if(path[path.size() -1] != '/') path = path.append("/");
        stack<string> name;
        auto l = path.begin(); 
        for(auto r = path.begin(); r != path.end(); ++r) {
            if((*l == '/' && *r != '/') || (l == r)) continue;
            else {
                string tmp(l, r);
                cout << l - path.begin() << " " << r - path.begin() << " ";
                cout << tmp << endl;
                if(tmp == "/..") {
                    if(!name.empty()) name.pop();
                } else if(!(tmp == "/" || tmp == "/.")) {
                    name.push(tmp);
                }
                l = r;
            }
        }
        string res;
        while(!name.empty()) {
            res = name.top() + res;
            name.pop();

        }
        return res.size() == 0? "/" : res;
    }
};

编辑距离

在这里插入图片描述

看到这道题的时候脑子一片空白,然后想到的是先求出最长公共字符串,可惜可惜啊,都想到最长公共字符串了,都和动态规划沾上关系了,我这竟然还没有想到就直接用动态规划解决,毕竟三种操作都放在那里,告诉你如何状态转移了,默哀三秒。

步入正题,这道题确实是有点意思的,对于动态规划来说,说不明显但是在用完之后怎么看怎么明显。首先明确状态为何?

定义状态数组dp[i][j]表示word1的第i个字符和word2的第j个字符的编辑距离。

初始化:当word1为0的时候,编辑距离就是word2的长度;同理当word2为0的时候,编辑距离就是word1的长度。

状态转移:dp[i][j]有三种方式可以到达,分别为word1插入,word1删除,word1替换。这里会有奇怪的地方,word1删除,岂不是不能做到,毕竟在走到dp[i][j]的时候是不知道dp[i+1][j]的值。但是word1删除等价于word2插入。所以三种操作代表三种转移方案。

状态转移方程:dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1。不过还是有需要注意的地方,就是替换的时候,如果两个字符串的第i-1个字符和第j-1个字符是相等的,那么我是不需要进行操作的。所以word1替换的操作需要根据字符是否相等来决定是否+1操作。

AC代码:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.size(), m = word2.size();
        if(n * m == 0) return n + m;
        int dp[n+1][m+1];
        //default
        memset(dp, 0, sizeof(dp));
        for(int i = 0; i < m+1; ++i) dp[0][i] = i;
        for(int i = 0; i < n+1; ++i) dp[i][0] = i;

        //State transition
        for(int i = 1; i < n+1; ++i) {
            for(int j = 1; j < m+1; ++j) {
                int inert = dp[i-1][j] + 1;
                int delet = dp[i][j-1] + 1;
                int replace = dp[i-1][j-1];
                if(word1[i-1] != word2[j-1]) replace += 1;
                dp[i][j] = min(inert, min(delet, replace));
            }
        }

        return dp[n][m];
    }
};

矩阵置零

在这里插入图片描述

第一反应就是利用两个set分别保存将要被置为0的行和列,然后看到题目要求:

进阶:

一个直接的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个常数空间的解决方案吗?

不错,第一反应就是一个简单的改进方案,囧。

然后苦苦思索常数空间,想到的一个方案就是在找到0之后先不置为0,用一个别的数字先代替,当然第一反应是-1,但是在写代码的时候又考虑到没说数组中不存在-1的数字,随推翻。然后再次思索,随失败。不甘心的参考了解题,神奇。

最后是通过以第一行第一列作为标记,然后单独考虑第一行第一列,最后虽然额外空间是常数空间,但是怎么都觉得这个代码比较臃肿。

AC代码:

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        bool fr = false, fc = false;
        int m = matrix.size();
        int n = (*matrix.begin()).size();
        //判断第一行是否存在0
        for(int i = 0; i < n; ++i) {
            if(matrix[0][i] == 0) {
                fr = true;
                break;
            }
        }
        //判断第一列是否存在0
        for(int j = 0; j < m; ++j) {
            if(matrix[j][0] == 0) {
                fc = true;
                break;
            }
        }
        //除了第一行第一列,遍历数组,如果存在0,则将此位置对应的首个行和列置为0
        for(int i = 1; i < m; ++i) {
            for(int j = 1; j < n; ++j) {
                if(matrix[i][j] == 0) {
                    matrix[0][j] = 0;
                    matrix[i][0] = 0;
                }
            }
        }
        //判断该位置的首个行和列是否为0,是则修改此位置为0
        for(int i = 1; i < m; ++i) {
            for(int j = 1; j < n; ++j) {
                if(matrix[0][j] == 0 || matrix[i][0] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        //单独考虑的第一行第一列
        if(fr) {
            for(int i = 0; i < n; ++i) {
                matrix[0][i] = 0;
            }
        }
        if(fc) {
            for(int j = 0; j < m; ++j) {
                matrix[j][0] = 0;
            }
        }
    }
};

搜索二维矩阵

在这里插入图片描述

看到排序数组,而且查找值,提高效率。不用思考就是二分查找,矩阵又如何,两次二分查找就可以解决了,先找行,再找列,找到返回true,否则返回false。

AC代码:

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size();
        if(m == 0) return false;
        int n = (*matrix.begin()).size() - 1;
        if(n == -1) return false;
        int targetm = 0, targetn = 0;
        while(targetm < m) {
            int midm = targetm + (m - targetm) / 2;
            if(matrix[midm][0] == target) return true;
            else if(matrix[midm][0] < target) {
                targetm = midm + 1;
            }else {
                m = midm;
            }
        }
        targetm = targetm == 0 ? 0 : targetm-1;
        while(targetn <= n) {
            int midn = targetn + (n - targetn) / 2;
            if(matrix[targetm][midn] == target) return true;
            else if(matrix[targetm][midn] < target) {
                targetn = midn + 1;
            }else {
                n = midn - 1;
            }
        }
        return false;
    }
};

PS,二维数组其实也是一维数组,所以这道题二分查找可以更简洁,left = 0;right = m*n-1;mid = left + (right - left) / 2,对应的值是matrix[mid/n][mid%n]

颜色分类

在这里插入图片描述

解题思路,这道题不就是排序吗,可以利用快速排序的思想,以1为基准,小于1的放左边,大于1的放右边。这样的话我们可以维护1的左右边界,遍历一次数组即可完成。

AC代码:

class Solution {
public:
    void sortColors(vector<int>& nums) {
        if(nums.size() < 2) return;
        //left记录1的左边界,right记录1的右边界
        int left = 0, right = nums.size() - 1;
        for(int i = 0; i <= right; ++i) {
            if(nums[i] == 0) {
                swap(nums[left++], nums[i]);
            }
            if(nums[i] == 2) {
                //i--保证从后面交换过来的数字再次判断是否为0
                swap(nums[i--], nums[right--]);
            }
        }
    }
};

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦