單序列與雙序列動態規劃經典題解

2021-03-02 超級碼厲

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:

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數組中,避免了重複計算,因此是個典型的動態規劃。抽象如下:

以下筆者的解法在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.

請朋友們思考,先給出解題模版:

(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)

今天把單序列與雙序列典型題目給了模版及題解,朋友是否理解了動態規劃思想呢?

接下來,背包型等動態規劃陸續分享。請關注公眾號: 超級碼厲。 

相關焦點

  • 動態規劃求解最長公共子序列
    最長公共子序列最長公共子序列,英文為Longest Common Subsequence,縮寫LCS。一個序列,如果是某兩個或多個已知序列的最長子序列,則稱為最長公共子序列。另外,要注意的是最長公共子序列與最長公共子串不一樣,下面看一個例子就明白。有序列S1和S2,其中S1=hello,S2=hero。那麼最長公共子序列為heo,而最長公共子串為he。
  • 經典動態規劃問題:最長公共/遞增子序列
    今天我們一起來看一下兩個僅有一"字"之差的經典的動態規劃問題:最後我們會以一道LeetCode題目為例子,看看如何在特定情況下把LCS歸約成LIS從而降低時間複雜度。在講具體問題之前我們先來舉個例子看一下什麼是子序列(subsequence)以及它和子數組(subarray)和子集(subset)之間的區別。
  • 算法總結:最長上升子序列(LIS)
    重點來了:解釋一下,對於第一點,遇到這類問題一般優先考慮二分,二分解決不了的話考慮動態規劃;對於第二點中的多維問題就是將輸入中的每個元素換成二維數組或三維數組,比如:354./)官方題解中描述的方法為動態規劃和貪心+二分,按照前面的思路我們先想二分。
  • LeetCode 例題精講 | 15 最長公共子序列:二維動態規劃的解法
    不過,打家劫舍問題是一維動態規劃問題,而還有很多題目屬於二維動態規劃。今天我們就以一道經典的「最長公共子序列」問題講解二維動態規劃的解法。最長公共子序列問題經典到什麼程度呢?經典到有自己的專用縮寫 —— LCS (Longest Common Subsequence)。
  • 動態規劃解最長公共子序列問題
    1 最長公共子序列問題概述1.1 問題定義序列2 動態規劃求解最長公共子序列問題    int** C=nullptr;    //輔助矩陣    int** src=nullptr;    //使用動態規劃方法計算C矩陣和src矩陣    void writeTable(Seq* seq1,Seq* seq2);    //使用回溯方法來得到所有的最長公共子序列    void createSubSeq(Seq*
  • 深入理解動態規划算法 | 最長公共子序列LCS
    前面三篇文章已經為大家介紹了利用動態規划算法解決問題的思路以及相關的代碼實現,最為核心的就是第一步利用數學中函數的思想來建立模型,然後求解問題。這三個問題構建的數學函數都有一個共同的特徵就是所構建的函數都是一元函數即y = f(x)。
  • 詳解最長公共子序列問題,秒殺三道動態規劃題目
    動態規劃系列問題也是一樣,尤其是子序列相關的問題。本文從「最長公共子序列問題」展開,總結三道子序列問題,解這道題仔細講講這種子序列問題的套路,你就能感受到這種思維方式了。最長公共子序列計算最長公共子序列(Longest Common Subsequence,簡稱 LCS)是一道經典的動態規劃題目,大家應該都見過:給你輸入兩個字符串s1和s2,請你找出他們倆的最長公共子序列,返回這個子序列的長度。
  • 3 套經典流瑜伽序列,練完太酸爽了!
    分享 3 套經典流瑜伽序列配合呼吸,專注意識練完簡直太酸爽了1、熱身序列這一套主要針對身體的熱身,能夠快速靈活關節、激活肌肉,進入練習狀態,練習前也可以先做3-5>額頭點地,保持3-5個呼吸手推地,起身進入四角跪姿吸氣抬頭卷尾骨,貓式呼氣低頭拱背,牛式配合呼吸,動態練習
  • 動態規劃刷題篇(1) 斐波那契、爬樓梯、路徑與最長遞增子序列
    509.https://leetcode-cn.com/problems/fibonacci-number這題作為動態規劃的入門題實在是再合適不過了,斐波那契數列其實每個同學在小學就接觸過,可能厲害的同學口算都會(這就是小學二年級的知識嗎)
  • 【時間序列】時間序列基本概念總結
    1.基本概念1.1 時間序列預測預測是商業中的常見統計任務,它可以為生產、運輸和人員安排等決策提供信息,並為長期戰略規劃提供指導。預測是指在考慮到所有可用信息的前提下,包括歷史數據和可以影響預測的任何未來事件的知識,儘可能準確地預言。而時間序列預測是指按照時間順序觀察事物的變換。
  • 斐波那契序列和盧卡斯序列
    時間序列今天我們來研究斐波那契序列和盧卡斯序列。當然,這兩種時間序列在數學原理上相似,同樣是常用也很經典的時間窗預測工具。
  • 經典算法研究總結(5)-動態規划算法解最長公共子序列LCS問題
    ok,咱們馬上進入面試題第56題的求解,即運用經典的動態規划算法:2.0、LCS問題描述56.最長公共子序列。題目:如果字符串一的所有字符按其在字符串中的順序出現在另外一個字符串二中,則字符串一稱之為字符串二的子串。注意,並不要求子串(字符串一)的字符必須連續出現在字符串二中。
  • 序列比對原理
    序列比對是非常基礎的工作,生物信息學分析大多涉及到這一步驟,雖然大部分人不做相關算法或軟體的開發,但是了解總體的算法原理還是需要的。    本文主要分為兩部分。第一部分介紹簡單的 2 條序列比對(多重序列比對不考慮),了解動態規劃策略如何尋找全局比對的解;第二部分介紹二代測序的 BWT + FM-index 策略如何實現降低資源消耗下的大規模數據比對。
  • 開髖不止有青蛙趴,還有這套動態開髖序列!(動圖)
    還有沒其他有趣味性一點的開髖串聯序列啊?其實,只要你細心留意,很多瑜伽體式都具有開髖功效噠!大家也可以結合自己的練習經驗嘗試看看能不能編排一些開髖序列,如果遇到不懂的歡迎諮詢小編噢!今天,小編為大家準備了一套以開髖為主題的流瑜伽序列,很多體式組合大家平時應該比較少這樣串聯,趕緊看看吧!
  • R時間序列分析(6)時間序列分解(下)
    (這一篇是用RMarkdown生成的word文檔直接導入公眾號,看看效果)時間序列的經典分解簡單易行,在許多教科書中佔有一席之地
  • 經典動態規劃:「換硬幣」系列三道問題詳解
    轉自面向大象編程換硬幣(Coin Change)問題是一道經典的動態規劃入門題,但是你可能不太知道,LeetCode 上的換硬幣問題其實是一個系列,共三道題目:第一道題是大家都熟悉的動態規劃入門題;第二道題變為求方案數,需要我們不重複不遺漏;第三道題更有難度,需要擴充為二維動態規劃,才能準確求出方案數量。沒有做過這個系列三道題的同學,不妨跟著本文看看三道題目的解法,理解其中的思路和技巧。下面我們一道一道地進行分析。
  • 序列DTFT的時域抽樣性質
    提倡「我為人人,人人為我」,歡迎廣大朋友提供好的資料、文章、題解和學習經驗,共同學習,共同進步。本書是數位訊號處理碩士研究生入學考試的解題指導, 對博士生入學考試也有一定的參考價值.
  • 磁共振檢查序列總結
    臨床應用: 擾相GRE-T1WI (1)腹部屏氣擾相GRE-T1WI 是臨床上最常用的腹部快速T1序列,掃描速度快,組織T1對比好,用於肝膽胰脾的平掃及動態增強 (2)擾相GRE序列MRA TOF-MRA/PC-MRA ;2D或3D成像
  • 動態規划算法入門,這就夠了
    動態規劃(Dynamic programming,簡稱DP),是大家都覺得比較難以掌握的算法。為了應付面試,我們經常會背誦一下斐波那楔數列或者背包問題的源碼,其實,只要理解了思想,掌握基本的模型,然後再來點寫代碼的套路,動態規劃並沒有那麼難。
  • 推薦狀態空間時間序列分析的兩本書
    統計學上,一個時間序列即是一個隨機過程的實現。時間序列按其統計特性可以分為平穩時間序列和非平穩時間序列兩類。在實際生活中遇到的序列,大多數是不平穩的。如果一個序列的平均值和方差始終為常數,則它是平穩的。在估計時間序列模型之前需把不平穩的時間序列轉化為平穩序列。