將一個單詞按照這種方式分:html
Below is one possible representation of s1 = "great"
:node
great / \ gr eat / \ / \ g r e at / \ a t
To scramble the string, we may choose any non-leaf node and swap its two children.ide
For example, if we choose the node "gr"
and swap its two children, it produces a scrambled string "rgeat"
.學習
rgeat / \ rg eat / \ / \ r g e at / \ a t
We say that "rgeat"
is a scrambled string of "great"
.大數據
Similarly, if we continue to swap the children of nodes "eat"
and "at"
, it produces a scrambled string "rgtae"
.idea
rgtae / \ rg tae / \ / \ r g ta e / \ t a
We say that "rgtae"
is a scrambled string of "great"
.spa
這樣就說他們是scrambled string。如今給你兩個字符串怎麼判斷是否屬於scrambled string呢.net
思路:code
想了n久,木有什麼好的idea。htm
而後學習了justdoit兄的博客,理解了後本身也跟着寫了個遞歸的。
簡單的說,就是s1和s2是scramble的話,那麼必然存在一個在s1上的長度l1,將s1分紅s11和s12兩段,一樣有s21和s22。
那麼要麼s11和s21是scramble的而且s12和s22是scramble的;
要麼s11和s22是scramble的而且s12和s21是scramble的。
判斷剪枝是必要的,不然過不了大數據集合。
class Solution { public: bool subScramble(string s1, string s2) { if (s1 == s2) return true; int len = s1.size(); string sort_s1 = s1, sort_s2 = s2; sort(sort_s1.begin(), sort_s1.end()); sort(sort_s2.begin(), sort_s2.end()); if (sort_s1 != sort_s2) return false; //若是字母不相等直接返回錯誤 for (int i = 1; i < len; ++i) { string s1_left = s1.substr(0,i); string s1_right = s1.substr(i); string s2_left = s2.substr(0,i); string s2_right = s2.substr(i); if (subScramble(s1_left, s2_left) && subScramble(s1_right, s2_right)) return true; s2_left = s2.substr(0, len - i); s2_right = s2.substr(len - i); if (subScramble(s1_left, s2_right) && subScramble(s1_right, s2_left)) return true; } return false; } bool isScramble(string s1, string s2) { return subScramble(s1, s2); } };
若是有更好的方法,咱們通常是不用遞歸的,由於遞歸每每複雜以至難以掌控。繼續學習,發現果真有動態規劃的方法。憑空想,確實不可思議。但看見以後又恍然明瞭。
本題遞歸複雜度是非多項式的。具體多少您探討一下。動態規劃的複雜度應該是O(n^4),連動態規劃都這個複雜度了,可見此題之可怕啊。
三維動態規劃:
dp[k][i][j]: s1的第i個開始長度爲k的串和s2的第j個開始長度爲k的串是否scrambled,是的話true,不然存false;
同遞歸核心思想,dp[k][i][j]應該能夠從1到k-1長度的分割來求解,一旦有一個爲true,那麼dp[k][i][j]就爲true並跳出。
即:
dp[k][i][j] = (dp[div][i][j] && dp[k-div][i+div][j+div] || dp[div][i][j+k-div] && dp[k-div][i+div][j]);
div從1到k-1,分兩種狀況,即s1的從i開始的div個和s2從j開始的div個scrambled而且從i+div開始的k-div個都匹配那麼true,
同理,若是s1的i開始的div個和s2的後div個匹配以及剩下另外兩部分也都匹配,那麼true。直到找到true位置。以下代碼,找到true後,for中的判斷!dp[k][i][j]不成立就跳出了。
class Solution { public: // 動態規劃 dp[k][i][j]存s1從第i個開始長度爲k的串和s2從j開始長度爲k的串是否scrambled bool isScramble(string s1, string s2) { if (s1.size() != s2.size()) return false; int len = s1.size(); bool dp[len+1][len][len]; // initialization for (int i = 0; i < len; ++i) for (int j = 0; j < len; ++j) dp[1][i][j] = s1[i] == s2[j]; // dp for (int k = 2; k < len + 1; ++k) for (int i = 0; i < len; ++i) for (int j = 0; j < len; ++j) { // initialization dp[k][i][j] = false; // once dp[k][i][j] is true, jump out for (int div = 1; div < k && !dp[k][i][j]; ++div) dp[k][i][j] = (dp[div][i][j] && dp[k-div][i+div][j+div] || dp[div][i][j+k-div] && dp[k-div][i+div][j]); } return dp[len][0][0]; } };
如下三點值得注意:
1. 此題遞歸只要主要判斷排序後子串是否相等,不等直接剪枝,減小計算量。
2. 兩中方法都用到的核心是把兩個字符串都分紅兩部分,若是對應匹配或者交叉匹配知足,則true。
3. 就是substr的用法,如何準確判斷子串。