leetcode-zgd-day56-583.两个字符串的删除操作/72.编辑距离/775.全局倒置与局部倒置

87 阅读1分钟

583.两个字符串的删除操作

题目链接:583. 两个字符串的删除操作 - 力扣(Leetcode)

解题思路:

 class Solution {
     public int minDistance(String word1, String word2) {
         /**
          * 动态规划
          * 1.dp[i][j] word1 0-i word2 0-j 两个字符串想要相同的最小步数
          * 2.if(word1.charAt(i) == word2.charAt(j)) dp[i][j] = dp[i - 1][j  - 1]
          *   else dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + 1;
          * 3.dp[0][j] dp[i][0]
          */
         int[][] dp = new int[word1.length() + 1][word2.length() + 1];
         int rcd = 1;
         boolean flag = false;
         for (int i = 0; i < word1.length(); i++){
             if(flag == false && word1.charAt(i) == word2.charAt(0)){
                 flag = true;
                 rcd = rcd - 2;
             }
             rcd++;
             dp[i][0] = rcd;
         }
         rcd = 1;
         flag = false;
         for (int i = 0; i < word2.length(); i++){
             if(flag == false && word2.charAt(i) == word1.charAt(0)){
                 flag = true;
                 rcd = rcd - 2;
             }
             rcd++;
             dp[0][i] = rcd;
         }
         for(int i = 1; i < word1.length(); i++){
             for(int j = 1; j < word2.length(); j++){
                 if(word1.charAt(i) == word2.charAt(j)) dp[i][j] = dp[i - 1][j  - 1];
                 else dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + 1;
             }
         }
         return dp[word1.length() - 1][word2.length() - 1];
     }
 }

72.编辑距离

题目链接:72. 编辑距离 - 力扣(Leetcode)

解题思路:

这个题的递推关系式,主要还是区分,当前末尾元素是否相等。相等自然就是两个字符串抛去末尾元素的最小操作数+1。

如果不相等就需要考虑,有多少种途径产生这种最小操作数。一种是通过变换末尾元素的方式,通过一次变化,可以使得末尾两个元素相等。也就是dp(i - 1)(j - 1) + 1。

还有就是通过删除的方式,删除有两种情况,一种是删除单词word1的最后一个元素,一种是删除单词word2的最后一个元素。也就对应着dp(i - 1)(j) + 1 和 dp(i)(j - 1) + 1两种情况。

取这三者的最小值,就是我们想要的dp(i)(j)。

再就是初始化的时候稍微有些麻烦需要注意一下。

 class Solution {
     public int minDistance(String word1, String word2) {
         /**
          * 动态规划
          * 这个题和583.两个字符串的删除操作的最大区别就在于多了一个替换操作
          * 本质上插入操作和删除操作是一样的没有区别,而一次替换操作可能代替两次删除操作。
          * 1.dp[i][j] word1 0-i 转化为 word2 0-j 的最小操作数
          * 2.if(word1.charAt(i) == word2.charAt(j)) dp[i][j] = dp[i - 1][j - 1];
          *   else Math.min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1);
          */
         if(word1.length() == 0) return word2.length();
         if(word2.length() == 0) return word1.length();
         int[][] dp = new int[word1.length() + 1][word2.length() + 1];
         int rcd = 0;
         boolean flag = false;
         for (int i = 0; i < word1.length(); i++) {
             if(flag == false && word1.charAt(i) == word2.charAt(0)){
                 flag = true;
             }else{
                 rcd++;
             }
             dp[i][0] = rcd;
         }
         rcd = 0;
         flag = false;
         for (int i = 0; i < word2.length(); i++) {
             // 这里要思考清楚:
             // 不需要加的情况就是第一次两个字符串中对应字符相等的时候,可以不自增。
             // 不要把判断条件搞的过于复杂。
             if(flag == false && word2.charAt(i) == word1.charAt(0)){
                 flag = true;
             }else{
                 rcd++;
             }
             dp[0][i] = rcd;
         }
         for(int i = 1; i < word1.length(); i++){
             for(int j = 1; j < word2.length(); j++){
                 if(word1.charAt(i) == word2.charAt(j)){
                     dp[i][j] = dp[i - 1][j - 1];
                 }else{
                     //Math.min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1, dp[i][j - 1] + 1);
                     dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1] + 1, dp[i - 1][j] + 1), dp[i][j - 1] + 1);
                 }
             }
         }
         return dp[word1.length() - 1][word2.length() - 1];
     }
 }

775.全局倒置与局部倒置

题目链接:775. 全局倒置与局部倒置 - 力扣(Leetcode)

解题思路:

解法一:数学归纳

 class Solution {
     public boolean isIdealPermutation(int[] nums) {
         for(int i = 0; i < nums.length; i++){
                 if(Math.abs(nums[i] - i) > 1) return false;
         }
         return true;
     }
 }

解法二:贪心

贪心贪的是数组里的任意下标i元素nums[i],只有i+1允许比他小,从i+2开始都不允许比他小,若存在,全局倒置就不等于局部倒置数量了。

 class Solution {
     public boolean isIdealPermutation(int[] nums) {
         int min = nums[nums.length - 1];
         for(int i = nums.length - 3; i >= 0; i--){
             if(nums[i] > min) return false;
             min = Math.min(nums[i + 1],min);
         }
         return true;
     }
 }

解法三:归并排序

归并排序,先将数组分为两部分,然后对两部分分别排序,然后再将两部分数组合并为一部分。重新赋值给原数组。

 class Solution {
 ​
     int p = 0, q = 0;
 ​
     public boolean isIdealPermutation(int[] nums) {
         // 归并排序计算逆序对个数
         for(int i = 0; i < nums.length - 1; i++){
             if(nums[i] > nums[i + 1]) p++;
         }
         merge(nums, 0, nums.length - 1);
         return p == q;
     }
 ​
     public void merge(int[] nums, int left, int right){
         // 递归终止条件
         if(left >= right) return;
         int mid = (left + right) >> 1;
         // 两部分先排序
         merge(nums, left, mid);
         merge(nums, mid + 1, right);
         // 合并两部分
         int[] rcd = new int[right - left + 1];
         int l = left, r = mid + 1, k = 0;
         while(l <= mid && r <= right){
             if(nums[l] <= nums[r]) rcd[k++] = nums[l++];
             else{
                 q += ((mid - l) + 1);
                 rcd[k++] = nums[r++];
             }
         }
         while(l <= mid) rcd[k++] = nums[l++];
         while(r <= right) rcd[k++] = nums[r++];
         for(l = left, k = 0; k < right - left + 1; k++, l++){
             nums[l] = rcd[k];
         }
     }
 }