单序列与双序列动态规划经典题解
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)
今天把单序列与双序列典型题目给了模版及题解,朋友是否理解了动态规划思想呢?
以上是关于单序列与双序列动态规划经典题解的主要内容,如果未能解决你的问题,请参考以下文章
算法 ---- 子序列系列问题题解(子序列编辑距离回文系列问题)
算法 ---- 子序列系列问题题解(子序列编辑距离回文系列问题)
算法 ---- 子序列系列问题题解(子序列编辑距离回文系列问题)