小明思考

          Just a software engineer
          posts - 124, comments - 36, trackbacks - 0, articles - 0
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          交叉字符串

          Posted on 2013-05-10 20:47 小明 閱讀(3238) 評(píng)論(4)  編輯  收藏 所屬分類: 數(shù)據(jù)結(jié)構(gòu)和算法
          問(wèn)題給定字符串s1,s2,s3,判斷s3是否可以由s1和s2交叉組成得到。

          例如:

          s1 = "aabcc",
          s2 = "dbbca",

          When s3 = "aadbbcbcac", return true.
          When s3 = "aadbbbaccc", return false.


          解法一:(遞歸)

          一個(gè)簡(jiǎn)單的想法,是遍歷s3的每個(gè)字符,這個(gè)字符必須等于s1和s2的某個(gè)字符。如果都不相等,則返回false
          我們使用3個(gè)變量i,j,k分別記錄當(dāng)前s1,s2,s3的字符位置。
          如果s3[k] = s1[i], i向后移動(dòng)一位。如果s3[k]=s2[j],j向后移動(dòng)一位。
          這個(gè)題目主要難在如果s1和s2的字符出現(xiàn)重復(fù)的時(shí)候,就有兩種情況,i,j都可以向后一位。
          下面的算法在這種情況使用了遞歸,很簡(jiǎn)單的做法。但是效率非常差,是指數(shù)復(fù)雜度的。

          public class Solution {
              public boolean isInterleave(String s1, String s2, String s3) {
                  int l1 = s1.length();
                  int l2 = s2.length();
                  int l3 = s3.length();
                  
                  if(l1+l2!=l3){
                      return false;
                  }
                  
                  char[] c1 = s1.toCharArray();
                  char[] c2 = s2.toCharArray();
                  char[] c3 = s3.toCharArray();
                  
                  int i=0,j=0;
                  for(int k=0;k<l3;++k){
                      char c = c3[k];
                      boolean m1 = i<l1 && c==c1[i];
                      boolean m2 = j<l2 && c==c2[j];
                      if(!m1 && !m2){
                          return false;
                      }
                      else if(m1 && m2){
                          String news3 =  s3.substring(k+1);
                          return isInterleave(s1.substring(i+1),s2.substring(j),news3)
                                          || isInterleave(s1.substring(i),s2.substring(j+1),news3);
                      }
                      else if(m1){
                          ++i;
                      }
                      else{
                          ++j;
                      }
                  }
                  
                  return true;        
              }
          }


          解法二:(動(dòng)態(tài)規(guī)劃)
          為了減少重復(fù)計(jì)算,就要使用動(dòng)態(tài)規(guī)劃來(lái)記錄中間結(jié)果。

          這里我使用了一個(gè)二維數(shù)組result[i][j]來(lái)表示s1的前i個(gè)字符和s2的前j個(gè)字符是否能和s3的前i+j個(gè)字符匹配。

          狀態(tài)轉(zhuǎn)移方程如下:
          result[i,j] = (result[i-1,j] && s1[i] = s3[i+j])  || (result[i,j-1] && s2[j] = s3[i+j]);
          其中0≤i≤len(s1) ,0≤j≤len(s2)

          這樣算法復(fù)雜度就會(huì)下降到O(l1*l2)
          public class Solution {
             
          public boolean isInterleave(String s1, String s2, String s3) {
                  int l1 = s1.length();
                  int l2 = s2.length();
                  int l3 = s3.length();
                 
                  if(l1+l2!=l3){
                      return false;
                  }
                  
                  char[] c1 = s1.toCharArray();
                  char[] c2 = s2.toCharArray();
                  char[] c3 = s3.toCharArray();
                  
                  boolean[][] result = new boolean[l1+1][l2+1];
                  result[0][0] = true;
                  
                  for(int i=0;i<l1;++i){
                      if(c1[i]==c3[i]){
                          result[i+1][0] = true;
                      }
                      else{
                          break;
                      }
                  }
                  
                  for(int j=0;j<l2;++j){
                      if(c2[j]==c3[j]){
                          result[0][j+1] = true;
                      }
                      else{
                          break;
                      }
                  }
                  
                  
                  for(int i=1;i<=l1;++i){
                      char ci = c1[i-1];
                      for(int j=1;j<=l2;++j){
                          char cj = c2[j-1];
                          char ck = c3[i+j-1];
                             result[i][j] = (result[i][j-1] && cj==ck) || (result[i-1][j] && ci==ck);
                      }
                  }
                  
                  return result[l1][l2];
             }
          }

          評(píng)論

          # re: 交叉字符串  回復(fù)  更多評(píng)論   

          2013-05-11 10:19 by 開(kāi)發(fā)吧
          今天正在搞這個(gè)問(wèn)題,謝謝啦!

          # re: 交叉字符串  回復(fù)  更多評(píng)論   

          2013-05-12 21:09 by tb
          算法 對(duì)我有點(diǎn)啟發(fā) 謝謝

          # re: 交叉字符串  回復(fù)  更多評(píng)論   

          2013-05-13 11:23 by javanewer
          boolean[][] result = new boolean[l1+1][l2+1];
          這一句什么作用?

          # re: 交叉字符串[未登錄](méi)  回復(fù)  更多評(píng)論   

          2013-05-15 18:57 by wang
          交叉字符串是用來(lái)干嘛的
          主站蜘蛛池模板: 定南县| 平邑县| 靖远县| 元朗区| 马龙县| 揭西县| 福贡县| 长汀县| 开封县| 石渠县| 剑河县| 榕江县| 普安县| 博白县| 吴旗县| 察雅县| 剑阁县| 新密市| 靖宇县| 博乐市| 邻水| 浏阳市| 上犹县| 札达县| 桃源县| 黄大仙区| 板桥市| 将乐县| 隆林| 泗水县| 南部县| 镇巴县| 天镇县| 桂阳县| 九龙坡区| 于田县| 望都县| 西乌珠穆沁旗| 高阳县| 石家庄市| 仪征市|