@@ -340,6 +340,352 @@ \subsubsection{代码}
340
340
\end {Code }
341
341
342
342
343
+ \subsubsection {相关题目 }
344
+ \begindot
345
+ \item 无
346
+ \myenddot
347
+
348
+
349
+ \section {Interleaving String } % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
350
+ \label {sec:interleaving-string }
351
+
352
+
353
+ \subsubsection {描述 }
354
+ Given $ s1 , s2 , s3 $ , find whether $ s3 $ is formed by the interleaving of $ s1 $ and $ s2 $ .
355
+
356
+ For example, Given: \code {s1 = "aabcc" , s2 = "dbbca" },
357
+
358
+ When \code {s3 = "aadbbcbcac" }, return true.
359
+
360
+ When \code {s3 = "aadbbbaccc" }, return false.
361
+
362
+
363
+ \subsubsection {分析 }
364
+ 这题用二维动态规划。
365
+
366
+ 设状态\fn {f[i][j]},表示\fn {s1[0,i]}和\fn {s2[0,j]},匹配\fn {s3[0, i+j]}。如果s1的最后一个字符等于s3的最后一个字符,则\fn {f[i][j]=f[i-1][j]};如果s2的最后一个字符等于s3的最后一个字符,则\fn {f[i][j]=f[i][j-1]}。因此状态转移方程如下:
367
+ \begin {Code }
368
+ f[i][j] = (s1[i - 1] == s3 [i + j - 1] and f[i - 1][j])
369
+ or (s2[j - 1] == s3 [i + j - 1] and f[i][j - 1]);
370
+ \end {Code }
371
+
372
+ \subsubsection {代码 }
373
+ \begin {Code }
374
+ // LeetCode, Interleaving String
375
+ // 动规,二维数组
376
+ class Solution {
377
+ public:
378
+ bool isInterleave(string s1, string s2, string s3) {
379
+ if (s3.length() != s1.length() + s2.length())
380
+ return false;
381
+
382
+ vector<vector<bool>> f(s1.length() + 1,
383
+ vector<bool>(s2.length() + 1, true));
384
+
385
+ for (size_t i = 1; i <= s1.length(); ++i)
386
+ f[i][0] = f[i - 1][0] and s1[i - 1] == s3[i - 1];
387
+
388
+ for (size_t i = 1; i <= s2.length(); ++i)
389
+ f[0][i] = f[0][i - 1] and s2[i - 1] == s3[i - 1];
390
+
391
+ for (size_t i = 1; i <= s1.length(); ++i)
392
+ for (size_t j = 1; j <= s2.length(); ++j)
393
+ f[i][j] = (s1[i - 1] == s3[i + j - 1] and f[i - 1][j])
394
+ or (s2[j - 1] == s3[i + j - 1] and f[i][j - 1]);
395
+
396
+ return f[s1.length()][s2.length()];
397
+ }
398
+ };
399
+ \end {Code }
400
+
401
+ \begin {Code }
402
+ // LeetCode, Interleaving String
403
+ // 动规,滚动数组
404
+ class Solution {
405
+ public:
406
+ bool isInterleave(string s1, string s2, string s3) {
407
+ if (s1.length() + s2.length() != s3.length())
408
+ return false;
409
+
410
+ if (s1.length() < s2.length())
411
+ return isInterleave(s2, s1, s3);
412
+
413
+ vector<bool> f(s2.length() + 1, true);
414
+
415
+ for (size_t i = 1; i <= s2.length(); ++i)
416
+ f[i] = s2[i - 1] == s3[i - 1] and f[i - 1];
417
+
418
+ for (size_t i = 1; i <= s1.length(); ++i) {
419
+ f[0] = s1[i - 1] == s3[i - 1] and f[0];
420
+
421
+ for (size_t j = 1; j <= s2.length(); ++j)
422
+ f[j] = (s1[i - 1] == s3[i + j - 1] and f[j])
423
+ or (s2[j - 1] == s3[i + j - 1] and f[j - 1]);
424
+ }
425
+
426
+ return f[s2.length()];
427
+ }
428
+ };
429
+ \end {Code }
430
+
431
+ \begin {Code }
432
+ // LeetCode, Interleaving String
433
+ // 递归,小集合可以过,大集合会超时
434
+ class Solution {
435
+ public:
436
+ bool isInterleave(string s1, string s2, string s3) {
437
+ if (s3.length() != s1.length() + s2.length())
438
+ return false;
439
+
440
+ return isInterleave(begin(s1), end(s1), begin(s2), end(s2),
441
+ begin(s3), end(s3));
442
+ }
443
+
444
+ template<typename InIt>
445
+ bool isInterleave(InIt first1, InIt last1, InIt first2, InIt last2,
446
+ InIt first3, InIt last3) {
447
+ if (first3 == last3)
448
+ return first1 == last1 and first2 == last2;
449
+
450
+ return (*first1 == *first3
451
+ and isInterleave(next(first1), last1, first2, last2,
452
+ next(first3), last3))
453
+ or (*first2 == *first3
454
+ and isInterleave(first1, last1, next(first2), last2,
455
+ next(first3), last3));
456
+ }
457
+ };
458
+ \end {Code }
459
+
460
+ \subsubsection {相关题目 }
461
+ \begindot
462
+ \item 无
463
+ \myenddot
464
+
465
+
466
+ \section {Scramble String } % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467
+ \label {sec:scramble-string }
468
+
469
+
470
+ \subsubsection {描述 }
471
+ Given a string $ s1 $ , we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
472
+
473
+ Below is one possible representation of \code {s1 = "great" }:
474
+ \begin {Code }
475
+ great
476
+ / \
477
+ gr eat
478
+ / \ / \
479
+ g r e at
480
+ / \
481
+ a t
482
+ \end {Code }
483
+
484
+ To scramble the string, we may choose any non-leaf node and swap its two children.
485
+
486
+ For example, if we choose the node \code {"gr"} and swap its two children, it produces a scrambled string \code {"rgeat"}.
487
+ \begin {Code }
488
+ rgeat
489
+ / \
490
+ rg eat
491
+ / \ / \
492
+ r g e at
493
+ / \
494
+ a t
495
+ \end {Code }
496
+
497
+ We say that \code {"rgeat"} is a scrambled string of \code {"great"}.
498
+
499
+ Similarly, if we continue to swap the children of nodes \code {"eat"} and \code {"at"}, it produces a scrambled string \code {"rgtae"}.
500
+ \begin {Code }
501
+ rgtae
502
+ / \
503
+ rg tae
504
+ / \ / \
505
+ r g ta e
506
+ / \
507
+ t a
508
+ \end {Code }
509
+
510
+ We say that \code {"rgtae"} is a scrambled string of \code {"great"}.
511
+
512
+ Given two strings $ s1 $ and $ s2 $ of the same length, determine if $ s2 $ is a scrambled string of $ s1 $ .
513
+
514
+
515
+ \subsubsection {分析 }
516
+ 首先想到的是递归(即深搜),对两个string进行分割,然后比较四对字符串。代码虽然简单,但是复杂度比较高。有两种加速策略,一种是剪枝,提前返回;一种是加缓存,缓存中间结果,和动规中自顶向下的记忆化搜索类似。
517
+
518
+ 剪枝可以五花八门,要充分观察,充分利用信息,找到能让节点提前返回的条件。例如,判断两个字符串是否互为scamble,至少要求每个字符在两个字符串中出现的次数要相等,如果不相等则返回false。
519
+
520
+ 加缓存,可以用数组或HashMap。本题维数较高,用HashMap,\fn {std::map}和\fn {std::unordered_map}均可。
521
+
522
+ 其次,这题可以用动规。
523
+
524
+ \subsubsection {代码 }
525
+
526
+ \begin {Code }
527
+ // LeetCode, Interleaving String
528
+ // 递归,小集合可以过,大集合会超时
529
+ class Solution {
530
+ public:
531
+ bool isScramble(string s1, string s2) {
532
+ return isScramble(s1.begin(), s1.end(), s2.begin());
533
+ }
534
+ private:
535
+ typedef string::iterator Iterator;
536
+ bool isScramble(Iterator first1, Iterator last1, Iterator first2) {
537
+ auto length = distance(first1, last1);
538
+ auto last2 = next(first2, length);
539
+
540
+ if (length == 1) return *first1 == *first2;
541
+
542
+ for (int i = 1; i < length; ++i)
543
+ if ((isScramble(first1, first1 + i, first2)
544
+ and isScramble(first1 + i, last1, first2 + i))
545
+ or (isScramble(first1, first1 + i, last2 - i)
546
+ and isScramble(first1 + i, last1, first2)))
547
+ return true;
548
+
549
+ return false;
550
+ }
551
+ };
552
+ \end {Code }
553
+
554
+ \begin {Code }
555
+ // LeetCode, Interleaving String
556
+ // 递归+剪枝
557
+ class Solution {
558
+ public:
559
+ bool isScramble(string s1, string s2) {
560
+ return isScramble(s1.begin(), s1.end(), s2.begin());
561
+ }
562
+ private:
563
+ typedef string::iterator Iterator;
564
+ bool isScramble(Iterator first1, Iterator last1, Iterator first2) {
565
+ auto length = distance(first1, last1);
566
+ auto last2 = next(first2, length);
567
+ if (length == 1) return *first1 == *first2;
568
+
569
+ // 剪枝,提前返回
570
+ int A[26]; // 每个字符的计数器
571
+ std::fill(A, A + 26, 0);
572
+ for(int i = 0; i < length; i++) A[*(first1+i)-'a']++;
573
+ for(int i = 0; i < length; i++) A[*(first2+i)-'a']--;
574
+ for(int i = 0; i < 26; i++) if (A[i] != 0) return false;
575
+
576
+ for (int i = 1; i < length; ++i)
577
+ if ((isScramble(first1, first1 + i, first2)
578
+ and isScramble(first1 + i, last1, first2 + i))
579
+ or (isScramble(first1, first1 + i, last2 - i)
580
+ and isScramble(first1 + i, last1, first2)))
581
+ return true;
582
+
583
+ return false;
584
+ }
585
+ };
586
+ \end {Code }
587
+
588
+ \begin {Code }
589
+ // LeetCode, Interleaving String
590
+ // 递归+map做cache
591
+ class Solution {
592
+ public:
593
+ bool isScramble(string s1, string s2) {
594
+ cache.clear();
595
+ return isScramble(s1.begin(), s1.end(), s2.begin());
596
+ }
597
+ private:
598
+ typedef string::const_iterator Iterator;
599
+ map<tuple<Iterator, Iterator, Iterator>, bool> cache;
600
+
601
+ bool isScramble(Iterator first1, Iterator last1, Iterator first2) {
602
+ auto length = distance(first1, last1);
603
+ auto last2 = next(first2, length);
604
+
605
+ if (length == 1) return *first1 == *first2;
606
+
607
+ for (int i = 1; i < length; ++i)
608
+ if ((cachedIsScramble(first1, first1 + i, first2)
609
+ and cachedIsScramble(first1 + i, last1, first2 + i))
610
+ or (cachedIsScramble(first1, first1 + i, last2 - i)
611
+ and cachedIsScramble(first1 + i, last1, first2)))
612
+ return true;
613
+
614
+ return false;
615
+ }
616
+
617
+ bool cachedIsScramble(Iterator first1, Iterator last1, Iterator first2) {
618
+ auto key = make_tuple(first1, last1, first2);
619
+ auto pos = cache.find(key);
620
+
621
+ return (pos != cache.end()) ?
622
+ pos->second : (cache[key] = isScramble(first1, last1, first2));
623
+ }
624
+ };
625
+ \end {Code }
626
+
627
+ \begin {Code }
628
+ typedef string::const_iterator Iterator;
629
+ typedef tuple<Iterator, Iterator, Iterator> Key;
630
+ // 定制一个哈希函数
631
+ namespace std {
632
+ template<> struct hash<Key> {
633
+ size_t operator()(const Key & x) const {
634
+ Iterator first1, last1, first2;
635
+ std::tie(first1, last1, first2) = x;
636
+
637
+ int result = *first1;
638
+ result = result * 31 + *last1;
639
+ result = result * 31 + *first2;
640
+ result = result * 31 + *(next(first2, distance(first1, last1)-1));
641
+ return result;
642
+ }
643
+ };
644
+ }
645
+
646
+ // LeetCode, Interleaving String
647
+ // 递归+unordered_map做cache,比map快
648
+ class Solution {
649
+ public:
650
+ unordered_map<Key, bool> cache;
651
+
652
+ bool isScramble(string s1, string s2) {
653
+ cache.clear();
654
+ return isScramble(s1.begin(), s1.end(), s2.begin());
655
+ }
656
+
657
+ bool isScramble(Iterator first1, Iterator last1, Iterator first2) {
658
+ auto length = distance(first1, last1);
659
+ auto last2 = next(first2, length);
660
+
661
+ if (length == 1)
662
+ return *first1 == *first2;
663
+
664
+ for (int i = 1; i < length; ++i)
665
+ if ((cachedIsScramble(first1, first1 + i, first2)
666
+ and cachedIsScramble(first1 + i, last1, first2 + i))
667
+ or (cachedIsScramble(first1, first1 + i, last2 - i)
668
+ and cachedIsScramble(first1 + i, last1, first2)))
669
+ return true;
670
+
671
+ return false;
672
+ }
673
+
674
+ bool cachedIsScramble(Iterator first1, Iterator last1, Iterator first2) {
675
+ auto key = make_tuple(first1, last1, first2);
676
+ auto pos = cache.find(key);
677
+
678
+ return (pos != cache.end()) ?
679
+ pos->second : (cache[key] = isScramble(first1, last1, first2));
680
+ }
681
+ };
682
+ \end {Code }
683
+
684
+ \begin {Code }
685
+
686
+ \end {Code }
687
+
688
+
343
689
\subsubsection {相关题目 }
344
690
\begindot
345
691
\item 无
0 commit comments