Skip to content

Commit f5f3a4d

Browse files
committed
Interleaving String, Scramble String
1 parent 69e536a commit f5f3a4d

File tree

2 files changed

+346
-0
lines changed

2 files changed

+346
-0
lines changed

C++/LeetCodet题解(C++版).pdf

16.3 KB
Binary file not shown.

C++/chapDynamicProgramming.tex

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,352 @@ \subsubsection{代码}
340340
\end{Code}
341341

342342

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+
343689
\subsubsection{相关题目}
344690
\begindot
345691
\item

0 commit comments

Comments
 (0)