小明思考

          Just a software engineer
          posts - 124, comments - 36, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          Scramble String

          Posted on 2013-05-22 22:25 小明 閱讀(6141) 評論(0)  編輯  收藏 所屬分類: 數據結構和算法
          Problem

          Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
          Below is one possible representation of s1 = "great":
              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.
          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".
              rgtae
             /    \
            rg    tae
           / \    /  \
          r   g  ta  e
                 / \
                t   a
          We say that "rgtae" is a scrambled string of "great".
          Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

           


          分析:

          這個問題是google的面試題。由于一個字符串有很多種二叉表示法,貌似很難判斷兩個字符串是否可以做這樣的變換。
          對付復雜問題的方法是從簡單的特例來思考,從而找出規律。
          先考察簡單情況:
          字符串長度為1:很明顯,兩個字符串必須完全相同才可以。
          字符串長度為2:當s1="ab", s2只有"ab"或者"ba"才可以。
          對于任意長度的字符串,我們可以把字符串s1分為a1,b1兩個部分,s2分為a2,b2兩個部分,滿足((a1~a2) && (b1~b2))或者 ((a1~b2) && (a1~b2))

          如此,我們找到了解決問題的思路。首先我們嘗試用遞歸來寫。


          解法一(遞歸)

          兩個字符串的相似的必備條件是含有相同的字符集。簡單的做法是把兩個字符串的字符排序后,然后比較是否相同。
          加上這個檢查就可以大大的減少遞歸次數。
          代碼如下:
          public boolean isScramble(String s1, String s2) {
                  int l1 = s1.length();
                  int l2 = s2.length();
                  if(l1!=l2){
                      return false;
                  }
                  if(l1==0){
                      return true;
                  }
                  
                  char[] c1 = s1.toCharArray();
                  char[] c2 = s2.toCharArray();
                  if(l1==1){
                      return c1[0]==c2[0];
                  }
                  Arrays.sort(c1);
                  Arrays.sort(c2);
                  for(int i=0;i<l1;++i){
                      if(c1[i]!=c2[i]){
                          return false;
                      }
                  }
                  
                  boolean result = false;
                  for(int i=1;i<l1 && !result;++i){
                      String s11 = s1.substring(0,i);
                      String s12 = s1.substring(i);
                      String s21 = s2.substring(0,i);
                      String s22 = s2.substring(i);
                      result = isScramble(s11,s21) && isScramble(s12,s22);
                      if(!result){
                          String s31 = s2.substring(0,l1-i);
                          String s32 = s2.substring(l1-i);
                          result = isScramble(s11,s32) && isScramble(s12,s31);
                      }
                  }
                  
                  return result;
              }

          解法二(動態規劃)
          減少重復計算的方法就是動態規劃。動態規劃是一種神奇的算法技術,不親自去寫,是很難完全掌握動態規劃的。

          這里我使用了一個三維數組boolean result[len][len][len],其中第一維為子串的長度,第二維為s1的起始索引,第三維為s2的起始索引。
          result[k][i][j]表示s1[i...i+k]是否可以由s2[j...j+k]變化得來。

          代碼如下,非常簡潔優美:

          public class Solution {
              public boolean isScramble(String s1, String s2) {
                  int len = s1.length();
                  if(len!=s2.length()){
                      return false;
                  }
                  if(len==0){
                      return true;
                  }
                  
                  char[] c1 = s1.toCharArray();
                  char[] c2 = s2.toCharArray();
                  
                  boolean[][][] result = new boolean[len][len][len];
                  for(int i=0;i<len;++i){
                      for(int j=0;j<len;++j){
                          result[0][i][j] = (c1[i]==c2[j]);
                      }
                  }
                  
                  for(int k=2;k<=len;++k){
                      for(int i=len-k;i>=0;--i){
                        for(int j=len-k;j>=0;--j){
                            boolean r = false;
                            for(int m=1;m<k && !r;++m){
                                r = (result[m-1][i][j] && result[k-m-1][i+m][j+m]) || (result[m-1][i][j+k-m] && result[k-m-1][i+m][j]);
                            }
                            result[k-1][i][j] = r;
                        }
                      }
                  }
                  
                  return result[len-1][0][0];
              }
          }
          主站蜘蛛池模板: 北宁市| 太白县| 祁连县| 福州市| 奉贤区| 贡嘎县| 白山市| 萨迦县| 根河市| 马龙县| 灵寿县| 静海县| 托克托县| 兖州市| 泰安市| 绵竹市| 南乐县| 江川县| 武鸣县| 洞口县| 彰武县| 图木舒克市| 铁力市| 高台县| 浠水县| 五大连池市| 富平县| 哈尔滨市| 皮山县| 衡东县| 思南县| 孙吴县| 长葛市| 丽江市| 崇信县| 山西省| 莲花县| 孝感市| 郯城县| 中江县| 荣昌县|