@@ -375,6 +375,11 @@ int lengthOfLIS1(vector<int>& nums) {
375375
376376#pragma region 复杂度O(n log n)
377377// 题目给了提示,看到log n可以考虑往二分查找之类的上凑
378+ // 思路:保存最大长度及其末尾节点,通过判断是否大于末尾节点来决定是否能够组成子序列
379+ // 又,既然不能保证当前的"最大长度"会不会被其他组合反超
380+ // 干脆把整个过程,每个长度都保存下来,构成单调栈
381+ // 一方面通过栈顶来决定是否能够组合成更长的子序列
382+ // 另一方面通过查找,找出比当前节点小的第一个末尾进行更新,以确保其他组合的机会
378383int lengthOfLIS(vector<int > &nums) {
379384 int size = nums.size();
380385 if (size < 2) {
@@ -413,53 +418,21 @@ int lengthOfLIS(vector<int> &nums) {
413418
414419> 给定一个**非空**字符串 *s* 和一个包含**非空**单词列表的字典 *wordDict*,判定 *s* 是否可以被空格拆分为一个或多个在字典中出现的单词。
415420
416- ```go
417- func wordBreak(s string, wordDict []string) bool {
418- // f[i] 表示前i个字符是否可以被切分
419- // f[i] = f[j] && s[j+1~i] in wordDict
420- // f[0] = true
421- // return f[len]
422-
423- if len(s) == 0 {
424- return true
425- }
426- f := make([]bool, len(s)+1)
427- f[0] = true
428- max,dict := maxLen(wordDict)
429- for i := 1; i <= len(s); i++ {
430- l := 0
431- if i - max > 0 {
432- l = i - max
433- }
434- for j := l; j < i; j++ {
435- if f[j] && inDict(s[j:i],dict) {
436- f[i] = true
437- break
438- }
439- }
440- }
441- return f[len(s)]
442- }
443-
444-
445-
446- func maxLen(wordDict []string) (int,map[string]bool) {
447- dict := make(map[string]bool)
448- max := 0
449- for _, v := range wordDict {
450- dict[v] = true
451- if len(v) > max {
452- max = len(v)
453- }
454- }
455- return max,dict
456- }
457-
458- func inDict(s string,dict map[string]bool) bool {
459- _, ok := dict[s]
460- return ok
421+ ```c++
422+ bool wordBreak(string s, vector<string> &wordDict) {
423+ unordered_set<string> wordDictSet{wordDict.begin(), wordDict.end()};
424+ vector<bool> dp(s.size() + 1);
425+ dp[0] = true;
426+ for (int i = 1; i <= s.size(); ++i) {
427+ for (int j = 0; j < i; ++j) {
428+ if (dp[j] && wordDictSet.find(s.substr(j, i - j)) != wordDictSet.end()) {
429+ dp[i] = true;
430+ break;
431+ }
432+ }
433+ }
434+ return dp.back();
461435}
462-
463436```
464437
465438小结
@@ -479,50 +452,23 @@ func inDict(s string,dict map[string]bool) bool {
479452> 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
480453> 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
481454
482- ``` go
483- func longestCommonSubsequence (a string , b string ) int {
484- // dp[i][j] a前i个和b前j个字符最长公共子序列
485- // dp[m+1][n+1]
486- // ' a d c e
487- // ' 0 0 0 0 0
488- // a 0 1 1 1 1
489- // c 0 1 1 2 1
490- //
491- dp := make ([][]int ,len (a)+1 )
492- for i := 0 ;i<=len (a);i++ {
493- dp[i]=make ([]int ,len (b)+1 )
494- }
495- for i := 1 ;i<=len (a);i++ {
496- for j := 1 ;j<=len (b);j++ {
497- // 相等取左上元素+1,否则取左或上的较大值
498- if a[i-1 ]==b[j-1 ] {
499- dp[i][j]=dp[i-1 ][j-1 ]+1
455+ ``` c++
456+ vector<vector<int >> dp (text1.size() + 1, vector<int >(text2.size() + 1));
457+ for (int i = 1; i <= text1.size(); ++i) {
458+ for (int j = 1; j <= text2.size(); ++j) {
459+ if (text1[ i - 1] == text2[ j - 1] ) {
460+ dp[ i] [ j ] = dp[ i - 1] [ j - 1 ] + 1;
500461 } else {
501- dp[i][j]= max (dp[i- 1 ][j],dp[i][j- 1 ])
462+ dp[ i] [ j ] = max(dp[ i - 1] [ j ] , dp[ i] [ j - 1 ] );
502463 }
503464 }
504465 }
505- return dp[len (a)][len (b)]
506- }
507- func max (a ,b int )int {
508- if a>b{
509- return a
510- }
511- return b
466+ return dp.back().back();
512467}
513468```
514469
515470注意点
516471
517- - go 切片初始化
518-
519- ``` go
520- dp := make ([][]int ,len (a)+1 )
521- for i := 0 ;i<=len (a);i++ {
522- dp[i]=make ([]int ,len (b)+1 )
523- }
524- ```
525-
526472- 从 1 开始遍历到最大长度
527473- 索引需要减一
528474
@@ -536,37 +482,30 @@ for i:=0;i<=len(a);i++ {
536482
537483思路:和上题很类似,相等则不需要操作,否则取删除、插入、替换最小操作次数的值+1
538484
539- ``` go
540- func minDistance (word1 string , word2 string ) int {
541- // dp[i][j] 表示a字符串的前i个字符编辑为b字符串的前j个字符最少需要多少次操作
542- // dp[i][j] = OR(dp[i-1][j-1],a[i]==b[j],min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1)
543- dp := make ([][]int ,len (word1)+1 )
544- for i := 0 ;i<len (dp);i++{
545- dp[i]=make ([]int ,len (word2)+1 )
546- }
547- for i := 0 ;i<len (dp);i++{
548- dp[i][0 ]=i
549- }
550- for j := 0 ;j<len (dp[0 ]);j++{
551- dp[0 ][j]=j
552- }
553- for i := 1 ;i<=len (word1);i++{
554- for j := 1 ;j<=len (word2);j++{
555- // 相等则不需要操作
556- if word1[i-1 ]==word2[j-1 ] {
557- dp[i][j]=dp[i-1 ][j-1 ]
558- }else { // 否则取删除、插入、替换最小操作次数的值+1
559- dp[i][j]=min (min (dp[i-1 ][j],dp[i][j-1 ]),dp[i-1 ][j-1 ])+1
485+ ```c++
486+ int minDistance(string word1, string word2) {
487+ // 删除与插入操作是等价的,修改word1和修改word2也是等价的
488+ // 故,实际操作只有在三种:
489+ // 1. 在word1插入,dp[i][j] = dp[i][j - 1] + 1
490+ // 2. 在word2插入,dp[i][j] = dp[i - 1][j] + 1
491+ // 3. 在word1修改,dp[i][j] = dp[i - 1][j - 1] + 1
492+ vector<vector<int>> dp(word1.size(), vector<int>(word2.size()));
493+ for (int i = 0; i < word1.size(); ++i) {
494+ dp[i][0] = i;
495+ }
496+ for (int i = 0; i < word2.size(); ++i) {
497+ dp[0][i] = i;
498+ }
499+ for (int i = 1; i <= word1.size(); ++i) {
500+ for (int j = 1; j <= word2.size(); ++j) {
501+ if (word1[i - 1] == word2[j - 1]) {
502+ dp[i][j] = dp[i - 1][j - 1];
503+ } else {
504+ dp[i][j] = min(min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
560505 }
561506 }
562507 }
563- return dp[len (word1)][len (word2)]
564- }
565- func min (a ,b int )int {
566- if a>b{
567- return b
568- }
569- return a
508+ return dp.back().back();
570509}
571510```
572511
@@ -582,35 +521,23 @@ func min(a,b int)int{
582521
583522思路:和其他 DP 不太一样,i 表示钱或者容量
584523
585- ``` go
586- func coinChange (coins []int , amount int ) int {
587- // 状态 dp[i]表示金额为i时,组成的最小硬币个数
588- // 推导 dp[i] = min(dp[i-1], dp[i-2], dp[i-5])+1, 前提 i-coins[j] > 0
589- // 初始化为最大值 dp[i]=amount+1
590- // 返回值 dp[n] or dp[n]>amount =>-1
591- dp := make ([]int ,amount+1 )
592- for i := 0 ;i<=amount;i++{
593- dp[i]=amount+1
594- }
595- dp[0 ]=0
596- for i := 1 ;i<=amount;i++{
597- for j := 0 ;j<len (coins);j++{
598- if i-coins[j]>=0 {
599- dp[i]=min (dp[i],dp[i-coins[j]]+1 )
524+ ``` c++
525+ int coinChange (vector<int >& coins, int amount) {
526+ // 初始化为amount + 1,如果最后大于amount说明无解
527+ // dp[ i] 表示金额为i时,所需最少硬币个数
528+ // dp[ i] = min(dp[ i] , dp[ i - coin] + 1)
529+ // dp[ i - coin] ,倒扣当前面额
530+ // 注意不要越界,i - coin >= 0
531+ vector<int > dp(amount + 1, amount + 1);
532+ dp[ 0] = 0;
533+ for (auto i = 1; i <= amount; ++i) {
534+ for (const auto &coin : coins) {
535+ if (i - coin >= 0) {
536+ dp[ i] = min(dp[ i] , dp[ i - coin] + 1);
600537 }
601538 }
602539 }
603- if dp[amount] > amount {
604- return -1
605- }
606- return dp[amount]
607-
608- }
609- func min (a ,b int )int {
610- if a>b{
611- return b
612- }
613- return a
540+ return dp.back() > amount ? -1 : dp.back();
614541}
615542```
616543
@@ -622,34 +549,30 @@ func min(a,b int)int{
622549
623550> 在 n 个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为 m,每个物品的大小为 A[i]
624551
625- ``` go
626- func backPack (m int , A [] int ) int {
552+ ```c++
553+ int backPack(int m, vector< int> &A) {
627554 // write your code here
628- // f[i][j] 前i个物品,是否能装j
629- // f[i][j] =f[i-1][j] f[i-1][j-a[i] j>a[i]
630- // f[0][0]=true f[...][0]=true
631- // f[n][X]
632- f := make ([][]bool ,len (A)+1 )
633- for i := 0 ;i<=len (A);i++{
634- f[i]=make ([]bool ,m+1 )
635- }
636- f[0 ][0 ]=true
637- for i := 1 ;i<=len (A);i++{
638- for j := 0 ;j<=m;j++{
639- f[i][j]=f[i-1 ][j]
640- if j-A[i-1 ]>=0 && f[i-1 ][j-A[i-1 ]]{
641- f[i][j]=true
555+ // dp[i][j] 前i个物品,是否能装j
556+ // dp[i - 1][j] == true? 则不需要第i个物品也能装满
557+ // dp[i - 1][j - A[i - 1]]? 腾出第i个物品的空间,剩下空间能否装满
558+ // dp[i][j] = dp[i - 1][j] or dp[i - 1][j - A[i - 1]]
559+ vector<vector<bool>> dp(A.size() + 1, vector<bool>(m + 1));
560+ dp[0][0] = true;
561+ for (int i = 1; i <= A.size(); ++i) {
562+ for (int j = 0; j <= m; ++j) {
563+ dp[i][j] = dp[i - 1][j];
564+ if (j - A[i - 1] >= 0 && dp[i - 1][j - A[i - 1]]) {
565+ dp[i][j] = true;
642566 }
643567 }
644568 }
645- for i := m;i>= 0 ;i-- {
646- if f[ len (A )][i] {
647- return i
569+ for (int i = m; i >= 0; ++i) {
570+ if (dp[A.size( )][i]) {
571+ return i;
648572 }
649573 }
650- return 0
574+ return 0;
651575}
652-
653576```
654577
655578### [ backpack-ii] ( https://www.lintcode.com/problem/backpack-ii/description )
@@ -659,31 +582,22 @@ func backPack (m int, A []int) int {
659582
660583思路:f[ i] [ j ] 前 i 个物品,装入 j 背包 最大价值
661584
662- ``` go
663- func backPackII (m int , A [] int , V [] int ) int {
585+ ``` c++
586+ int backPackII (int m, vector< int > &A, vector< int > &V) {
664587 // write your code here
665- // f[i][j] 前i个物品,装入j背包 最大价值
666- // f[i][j] =max(f[i-1][j] ,f[i-1][j-A[i]]+V[i]) 是否加入A[i]物品
667- // f[0][0]=0 f[0][...]=0 f[...][0]=0
668- f := make ([][]int ,len (A)+1 )
669- for i := 0 ;i<len (A)+1 ;i++{
670- f[i]=make ([]int ,m+1 )
671- }
672- for i := 1 ;i<=len (A);i++{
673- for j := 0 ;j<=m;j++{
674- f[i][j]=f[i-1 ][j]
675- if j-A[i-1 ] >= 0 {
676- f[i][j]=max (f[i-1 ][j],f[i-1 ][j-A[i-1 ]]+V[i-1 ])
588+ // dp[ i] [ j ] 前i个物品,装入j背包 最大价值
589+ vector<vector<int >> dp(A.size() + 1, vector<int >(V.size() + 1));
590+ for (int i = 1; i <= A.size(); ++i) {
591+ for (int j = 0; j <= m; ++j) {
592+ // 不选第i个物品,则dp[ i] [ j ] = dp[ i - 1] [ j ]
593+ // 选了第i个物品,则dp[ i] [ j ] = dp[ i - 1] [ j - A[ i - 1]] ,倒扣i的大小
594+ dp[ i] [ j ] = dp[ i - 1] [ j ] ;
595+ if (j - A[ i - 1] >= 0) {
596+ dp[ i] [ j ] = max(dp[ i] [ j ] , dp[ i - 1] [ j - A[ i - 1]] + V[ i - 1] );
677597 }
678598 }
679599 }
680- return f[len (A)][m]
681- }
682- func max (a ,b int )int {
683- if a>b{
684- return a
685- }
686- return b
600+ return dp.back().back();
687601}
688602```
689603
0 commit comments