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.编辑距离
解题思路:
这个题的递推关系式,主要还是区分,当前末尾元素是否相等。相等自然就是两个字符串抛去末尾元素的最小操作数+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];
}
}
}