单序列与双序列动态规划经典题解

Posted 超级码厉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单序列与双序列动态规划经典题解相关的知识,希望对你有一定的参考价值。

reference: nine chapter algrithm


本篇给出一道单序列动态规划题解的详细分析过程,再给一道双序列型动态规划,最后留一道思考题。


Word Break

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

Note:

  • The same word in the dictionary may be reused multiple times in the segmentation.

  • You may assume the dictionary does not contain duplicate words.

Example 1:

Input: s = "leetcode", wordDict = ["leet", "code"]Output: trueExplanation: Return true because "leetcode" can be segmented as "leet code".

Example 2:

Input: s = "applepenapple", wordDict = ["apple", "pen"]Output: trueExplanation: Return true because "applepenapple" can be segmented as "apple pen apple".              

Note that you are allowed to reuse a dictionary word.

Example 3:

Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]Output: false


上述题意是给定一个字符串,能否被切割成给成一个或者多个单词,且单词必须是给定的字典中的词。


数学归纳法思路分解:

#1  给定字符串 s ="xxxxxxixxxxxxxxxx"; 如何判定 s.charAt(0) s.charAt(i)子字符串能不能被完美切割?

#2 考虑一种最简单情况: 假定0 到 i 的子字符串能被切割成两个单词,那么该字符串就能被完美切割。

#3 如何找到切割点呢?从0到i一次遍历即可。

找到一个j, 满足  0 < j < i ,  只要s.substring(1,j) s.substring(j+1,i) 两个子字符串在给定的词典中,那么该字符串就能完美切割。

上述计算过程,我们用一个boolean型一维数组f[s.length() + 1]来记录计算过程中的结果

f[i] = true表示从0到i的子字符串能被完美切割,否则 f[i] = false。

#4 以上述例子开始推导  "leetcode", wordDict = ["leet", "code"]

boolean f[] = new boolean[9];  f[0] = true 表示空字符能被完美切割。

从左往右开始推导。

子字符串 substring(1,1) = "l", 不在词典中,f[1] = false;

子字符串 substring(1,2) = "le",  判断两种情况:

(1) 分别判断“l”和“e”是否在词典中

(2)判断"le"是否在词典中

上述都不在,所以f[2] = false

子字符串 substring(1,3) = "lee", 下列情况:

(1) “l”,“ee”都不在字典中

(2)“le”,“e”都不在字典中

(3) “lee” 不在字典中

f[3] = false;

子字符串 substring(1,4) = "leet", 下列情况:

(1) “l”,“ee”都不在字典中

(2)“le”,“e”都不在字典中

(3) “lee”,"t" 都不在字典中

  (4)  “leet” 在字典中,所以f[4] = true;

依次类推即可。 上述过程因为已经把部分计算子结构放在了boolean数组中,避免了重复计算,因此是个典型的动态规划。抽象如下:

  • state: f[i]表示“前i”个字符能否被完美切分

  • function: f[i] = OR{f[j] && j+1 ~ i is a word}, 其中 j < i

  • initialize: f[0] = true

  • answer: f[n]


以下笔者的解法在leetcode上的运行时间是4毫秒,击败91.33%的submission。

Runtime: 4 ms, faster than 91.33% of Java online submissions for Word Break.


class Solution {public boolean wordBreak(String s, List<String> wordDict) {  if(s == null || s.length() < 1) {      return true; }
int length = s.length();  boolean[] f = new boolean[length + 1]; f[0] = true;
  for(int i = 1; i <= length; i++) {    for(int j = 0; j < i; j++) {      String sb = s.substring(j, i);        if(f[j] == true) {          if(wordDict.contains(sb)) {            f[i] = true;            break//避免重复计算          }        }     } }
  return f[length]; }
}



再来一个双序列动态规划算法

Given two strings, find the longest common subsequence (LCS).

Your code should return the length of LCS.

Example 1: Input:  "ABCD" and "EDCA" Output:  1 Explanation: LCS is 'A' or  'D' or 'C' Example 2: Input: "ABCD" and "EACB" Output:  2 Explanation: LCS is "AC"

问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。(来源:百度)

双序列动态规划思考模版

  • state: f[i][j]代表了第一个sequence的前i个数字/字符,配上第二个sequence 的前j个...

  • function: f[i][j] = 研究第i个和第j个的匹配关系

  • initialize: f[i][0] 和 f[0][i]

  • answer: f[n][m]  n = s1.length()  m = s2.length()

Longest Common Subsequence 模版

  • state: f[i][j]表示前i个字符配上前j个字符的LCS的长度

  • function: f[i][j] = MAX(f[i-1][j], f[i][j-1], f[i-1][j-1] + 1) // A[i - 1] == B[j - 1]

  • = MAX(f[i-1][j], f[i][j-1])

  • intialize: f[i][0] = 0 f[0][j] = 0

  • answer: f[n][m]


java实现:


public class LCS { public int lcs(String str1, String str2) {   if(str1 == null || str2 == null || str1.length() == 0 || str2.length() == 0) {       return 0;   }   int lengthOfS1 = str1.length();   int lengthOfS2 = str2.length();   int[][] f = new int[lengthOfS1 + 1][lengthOfS1 + 1];
   for(int i = 0; i <= lengthOfS1; i++)     for(int j = 0; j <= lengthOfS2;j++) {       if(i == 0 || j == 0) {           f[i][j] = 0;       }       else if(str1.charAt(i-1) == str2.charAt(j-1)) {           f[i][j] = f[i-1][j-1] + 1;       }       else {           f[i][j] = Math.max(f[i][j-1],f[i-1][j]);       }   }   return f[lengthOfS1][lengthOfS2]; }
}



类似的题:

Interleaving String,

Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.

For example,

Given:

s1 = "aabcc",

s2 = "dbbca",

When s3 = "aadbbcbcac", return true.

When s3 = "aadbbbaccc", return false.

请朋友们思考,先给出解题模版:

  • state: f[i][j]表示s1的前i个字符和s2的前j个字符能否交替组成s3的前i+j个字 符

  • function: f[i][j] = (f[i-1][j] && (s1[i-1]==s3[i+j-1]) ||

(f[i][j-1] && (s2[j-1]==s3[i+j-1])

  • initialize: f[i][0] = (s1[0..i-1] == s3[0..i-1])

  • f[0][j] = (s2[0..j-1] == s3[0..j-1])

  • answer: f[n][m], n = sizeof(s1), m = sizeof(s2)

今天把单序列与双序列典型题目给了模版及题解,朋友是否理解了动态规划思想呢?





以上是关于单序列与双序列动态规划经典题解的主要内容,如果未能解决你的问题,请参考以下文章

1006 最长公共子序列Lcs(经典动态规划)

算法 ---- 子序列系列问题题解(子序列编辑距离回文系列问题)

算法 ---- 子序列系列问题题解(子序列编辑距离回文系列问题)

算法 ---- 子序列系列问题题解(子序列编辑距离回文系列问题)

动态规划线性dp问题总结:数字三角形最长上升子序列最长公共子序列最短编辑距离 题解与模板

Java 求解最长回文子序列