动态规划(1)

381 阅读2分钟

Day05 动态规划-编辑距离问题

1、编辑距离

莱文斯坦距离

  • 允许增加、删除、替换字符这三个编辑操作。
  • 莱温斯坦距离的大小,表示两个字符串差异的大小
  • 思路:
    • 回溯。取a,b两指针分别指向s1、s2。
      • 当a,b指针位置相同时,继续向后遍历
      • 不同时,回溯三种情况。增加节点、删除节点、替换节点
      class Solution {
        public int minDistance(String word1, String word2) {
            return dfs(word1, word2, 0, 0);
        }
      
        private int dfs(String word1, String word2, int a, int b) {
            if (a > word1.length() - 1) {
                return Math.max(0,word2.length() - b);
            }
            if (b > word2.length() - 1) {
                return Math.max(0,word1.length() - a);
            }
            char target = word1.charAt(a);
            char c = word2.charAt(b);
            if (target == c) return dfs(word1, word2, a + 1, b + 1);
            else {
                return Math.min(
                        dfs(word1, word2, a + 1, b + 1),
                        Math.min(dfs(word1, word2, a + 1, b), dfs(word1, word2, a, b + 1))
                ) + 1;
            }
        }
      }
      
    • 优化-记忆化搜索
      • 回溯超时-有很多重叠子问题。
      • 引入dp数组。dp[i][j]代表i、j节点匹配的记录结果。
      class Solution {
       int[][] dp;
       public int minDistance(String word1, String word2) {
           dp = new int[word1.length()][word2.length()];
           return dfs(word1, word2, 0, 0);
       }
      
       private int dfs(String word1, String word2, int a, int b) {
           if (a > word1.length() - 1) {
               return Math.max(0,word2.length() - b);
           }
           if (b > word2.length() - 1) {
               return Math.max(0,word1.length() - a);
           }
           if (dp[a][b] != 0) return dp[a][b];
           char target = word1.charAt(a);
           char c = word2.charAt(b);
           if (target == c) dp[a][b] = dfs(word1, word2, a + 1, b + 1);
           else {
               dp[a][b] = Math.min(
                       dfs(word1, word2, a + 1, b + 1),
                       Math.min(dfs(word1, word2, a + 1, b), dfs(word1, word2, a, b + 1))
               ) + 1;
           }
           return dp[a][b];
       }
      }
      
    • 动态规划
      • 有重叠子问题
      class Solution {
      
       public int minDistance(String word1, String word2) {
           int m = word1.length();
           int n = word2.length();
           int[][] dp = new int[m + 1][n + 1];
           //dp[i][j]代表 s1只拿前i个字符,编辑成s2的前j个字符的最小代价
           //当s1为空时,最小操作次数为s2的长度
           for (int i = 1; i <= n; i++) {
               dp[0][i] = i;
           }
           //当s2为空时,最小操作次数为s1的长度
           for (int i = 1; i <= m; i++) {
               dp[i][0] = i;
           }
           for (int i = 1; i <= m; i++) {
               for (int j = 1; j <= n; j++) {
                   if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                       dp[i][j] = dp[i - 1][j - 1];
                   } else {
                       dp[i][j] = Math.min(
                               dp[i - 1][j],
                               Math.min(dp[i][j - 1], dp[i - 1][j - 1])
                       ) + 1;
                   }
               }
           }
           return dp[m][n];
       }
      }
      

2、最长公共子序列

  • 只允许增加、删除字符这两个编辑操作。
  • 最长公共子串的大小,表示两个字符串相似程度的大小
  • 思路:动态规划
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
		int m = text1.length();
		int n = text2.length();
		//dp[i][j] 表示长度为i的s1和长度为j的s2的最长公共子序列
		int[][] dp = new int[m + 1][n + 1];
		//当s1的长度为1时,dp[1][j] 最大长度为1
		for (int j = 1; j <= n; j++) {
			if (text1.charAt(0) == text2.charAt(j - 1)){
				dp[1][j] = 1;
			}else {
				dp[1][j] = dp[1][j - 1];
			}
		}
		for (int i = 1; i <= m; i++) {
			if (text1.charAt(i - 1) == text2.charAt(0)){
				dp[i][1] = 1;
			}else {
				dp[i][1] = dp[i - 1][1];
			}
		}
		for (int i = 2; i <= m; i++) {
			char target = text1.charAt(i - 1);
			for (int j = 2; j <= n; j++) {
				if (text2.charAt(j - 1) == target){
					dp[i][j] = dp[i - 1][j - 1] + 1;
				}else {
					dp[i][j] = Math.max(dp[i][j - 1],dp[i - 1][j]);
				}
			}
		}
		return dp[m][n];
	}
}