@@ -375,6 +375,11 @@ int lengthOfLIS1(vector<int>& nums) {
375
375
376
376
#pragma region 复杂度O(n log n)
377
377
// 题目给了提示,看到log n可以考虑往二分查找之类的上凑
378
+ // 思路:保存最大长度及其末尾节点,通过判断是否大于末尾节点来决定是否能够组成子序列
379
+ // 又,既然不能保证当前的"最大长度"会不会被其他组合反超
380
+ // 干脆把整个过程,每个长度都保存下来,构成单调栈
381
+ // 一方面通过栈顶来决定是否能够组合成更长的子序列
382
+ // 另一方面通过查找,找出比当前节点小的第一个末尾进行更新,以确保其他组合的机会
378
383
int lengthOfLIS(vector<int > &nums) {
379
384
int size = nums.size();
380
385
if (size < 2) {
@@ -413,53 +418,21 @@ int lengthOfLIS(vector<int> &nums) {
413
418
414
419
> 给定一个**非空**字符串 *s* 和一个包含**非空**单词列表的字典 *wordDict*,判定 *s* 是否可以被空格拆分为一个或多个在字典中出现的单词。
415
420
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();
461
435
}
462
-
463
436
```
464
437
465
438
小结
@@ -479,50 +452,23 @@ func inDict(s string,dict map[string]bool) bool {
479
452
> 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
480
453
> 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
481
454
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;
500
461
} 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 ] );
502
463
}
503
464
}
504
465
}
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();
512
467
}
513
468
```
514
469
515
470
注意点
516
471
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
-
526
472
- 从 1 开始遍历到最大长度
527
473
- 索引需要减一
528
474
@@ -536,37 +482,30 @@ for i:=0;i<=len(a);i++ {
536
482
537
483
思路:和上题很类似,相等则不需要操作,否则取删除、插入、替换最小操作次数的值+1
538
484
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;
560
505
}
561
506
}
562
507
}
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();
570
509
}
571
510
```
572
511
@@ -582,35 +521,23 @@ func min(a,b int)int{
582
521
583
522
思路:和其他 DP 不太一样,i 表示钱或者容量
584
523
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);
600
537
}
601
538
}
602
539
}
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();
614
541
}
615
542
```
616
543
@@ -622,34 +549,30 @@ func min(a,b int)int{
622
549
623
550
> 在 n 个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为 m,每个物品的大小为 A[i]
624
551
625
- ``` go
626
- func backPack (m int , A [] int ) int {
552
+ ```c++
553
+ int backPack(int m, vector< int> &A) {
627
554
// 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;
642
566
}
643
567
}
644
568
}
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;
648
572
}
649
573
}
650
- return 0
574
+ return 0;
651
575
}
652
-
653
576
```
654
577
655
578
### [ backpack-ii] ( https://www.lintcode.com/problem/backpack-ii/description )
@@ -659,31 +582,22 @@ func backPack (m int, A []int) int {
659
582
660
583
思路:f[ i] [ j ] 前 i 个物品,装入 j 背包 最大价值
661
584
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) {
664
587
// 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] );
677
597
}
678
598
}
679
599
}
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();
687
601
}
688
602
```
689
603
0 commit comments