隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
          數據加載中……

          有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數

          本文為原創,如需轉載,請注明作者和出處,謝謝!

          最近看了有道出的幾個復賽題,覺得很好玩,現給出Java版的答案。先看看提干部分

              如果一個數字十進制表達時,不存在連續兩位數字相等,則稱之為“不重復數”。例如,105,1234和12121都是“不重復數”,而11,100和 1225不算。給定一個long類型數字A,返回大于A的最小“不重復數”。 下面是幾個測試用例,我又加了幾個

          Examples:
          0)    54
              returns: 56
              大于54的最小數字是55,但55不是“不重復數”。下一個數字是56,它滿足條件。

          1)    10
              returns: 12

          2)    9
              returns: 10

          3)    98
              returns: 101
              99和100都不是“不重復數”, 101是。

          4)    21099
              returns: 21201

          5)  99123
              returns: 101010

          6)  1134567
              returns: 1201010

          ok,現在看看解題思路,其實這個題單純從題本身上看并不復雜,主要是效率問題。估計不會有人一個數一個數地循環查找吧,那樣如果給定的long數很大時,可能會進行成千上萬次的循環,會很慢很慢。技巧還是有的,現在來看看怎么快速搞定這個問題。
            
              首先來拿一個例子看,就選 21099了。
              這個數低兩位(99)是重復的。既然要找比21099大的最新不重復數,就需要從這兩位開始遞增,但不是逐1的遞增。比21099大的數是21100,這個數也是個重復的數,有兩對重復的(11和00)。從右側開始處理它們。先處理00。我們現在要做的就是把00變成不重復的,很簡單,就成01就可以了。現在21100就變成了21101,這個數還是有兩個重復數(11)。現在處理11,把11變成12就不重復的,那么現在21101就變成了21201,ok,現在就得到了最終結果。我們看看,只結果了兩步,是很快地,因此,我們可以總結一下算法,步驟如下:
          1.  將給定的long數加1。
          2.  從這個數開始檢測是否為重復數,如果不是,ok,這個數就是最終結果。如果是,那么從數的右側開始找第1對重復的數,然后將其加1,得到一個新的數。
          3.  然后用這個數再從第2步開始。
          這個算法首先需要編寫一個方法用于將給定數最右側第一對重復的數找出,并且加1,最后得到一個新的數。如果這個數是不重復的數,那么直接返回0。代碼如下:

              // sb表示指定的數,以StringBuilder對象表示
              public static long getNextNum(StringBuilder sb)
              {
                  String result 
          = "";
                  
          char c = 'a'// c表示數字中待檢測位中高位的字符
                  int i = 0;
                  
          for (i = sb.length() - 1; i >= 0; i--)
                  {
                      
          // 如果相鄰的兩個數字不相同,那么將當前字符保存在c中
                      if (sb.charAt(i) != c)
                      {
                          c 
          = sb.charAt(i);
                      }
                      
          // 如果相鄰的兩個數字相同,那進行下一步地處理
                      else
                      {
                          
          // 將相同的兩個數字組成的數加1
                          long n = Long.parseLong(String.valueOf(c) + String.valueOf(c)) + 1;
                          
          // 先將這兩個相同的數字的位置的值設為0,以便進行相加

                          
          // 計算數字后面要補的0的數,如21443中重復的數字是44,加1后是45,那么首先將44設成00,
                          
          // 也就是21003,然后將45后面補0,就是450,最后用21003和450相加,就變成了21453
                          int m = sb.length() - i - 2;
                          sb.setCharAt(i, 
          '0');
                          sb.setCharAt(i 
          + 1'0');
                          
          for (int k = 0; k < m; k++)
                              n 
          *= 10;
                          
          long num = Long.parseLong(sb.toString()) + n;
                          sb 
          = new StringBuilder(String.valueOf(num));
                         
          // 開始將重復數后面的數變成最小的
                          m = i + 2;
                          
          for (int x = m; x < sb.length(); x++)
                          {
                              
          for (int y = 0; y < 10; y++)
                              {
                                  
                                  
          if (sb.charAt(x - 1!= (y + 48))
                                  {
                                      sb.setCharAt(x, (
          char) (y + 48));
                                      
          break;
                                  }
                              }
                          }

                          
          return Long.parseLong(sb.toString());
                      }
                  }
                  
          return 0;
              }
              要注意的是,雖然把每一對重復的數都變成了不重復的,但仍然不是最小的數,需要將當前重復數后面的數變成最小的,例如,99123將99變成不重復的數,也就是100,原來的數變成了100123,但100123還需要繼續變成100101,再查找重復數,把到了00,再變成101101,然后再變成101010,就ok了。
              最后調用getNextNum方法來返回最終結果,代碼如下:
              public static long getMinNoRepetitionNum(long num)
              {
                  String s 
          = String.valueOf(num + 1);

                  
          long n = 0;
                  
          long result = 0;
                  
          while ((n = getNextNum(new StringBuilder(s))) != 0)
                  {
                      s 
          = String.valueOf(n);
                      result 
          = n;
                  }
                  
          if (result == 0)
                      
          return num + 1;
                  
          else
                      
          return result;
              }

              現在可以使用下面的代碼來測試一下:
          System.out.println(getMinNoRepetitionNum(1999));




          Android開發完全講義(第2版)(本書版權已輸出到臺灣)

          http://product.dangdang.com/product.aspx?product_id=22741502



          Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


          新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

          posted on 2010-05-11 16:23 銀河使者 閱讀(3586) 評論(21)  編輯  收藏 所屬分類: javaalgorithm 原創

          評論

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          算法錯了,20097怎么辦?
          按你算法是20198
          實際應該是20101
          2010-05-12 09:32 | zack

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數1  回復  更多評論   

          @zack
          少加了些代碼,現在ok了。
          2010-05-12 09:53 | 銀河使者

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          這個題如果給我做的話我不會從左往右檢查,相反我會從右往左檢查
          1.將數字+1
          2. 從左往右(從高位到低位)判斷是否有重復數字,沒有的話進入4檢查到第一組重復數字進入3
          2. 將兩個重復數字的以前的(包括重復數字取出來)并且+1,將結果*(10^n) + 01序列
          3. 輸出結果
          例 21001224321
          1.加1 = 21001224322
          2. 是重復數字進入3
          3. 2100 *10^7 + 1* 10^7 + 1* 10^5 + 1* 10^3 + 1* 10^1 =
          21010101010
          4. 輸出 21010101010
          2010-05-12 18:24 | Zhjiang

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          @Zhjiang
          你寫的算術公式真的能算出這21010101010個數???

          我都不知道你怎么算出來的。
          2010-05-13 14:29 | Lancelot

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          @Lancelot
          試試不用知道了,計算機會告訴你的。
          2010-05-13 15:29 | 銀河使者

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          @銀河使者
          你知道10^7等于多少嗎???
          你把
          2100 *10^7 + 1* 10^7 + 1* 10^5 + 1* 10^3 + 1* 10^1
          算出來,你能等于21010101010嗎???

          拜托別說不負責任的話,只會讓自己獻丑。
          2010-05-13 16:33 | Lancelot

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          @銀河使者
          而且這個算式也與他上面寫的:
          2. 將兩個重復數字的以前的(包括重復數字取出來)并且+1,將結果*(10^n) + 01序列
          這句不符,明顯算式里沒有出現括號,那到底是先乘再取余(結果:21003)還是先取余再乘(結果:27348)?

          拜托你幫我算算是多少。
          在線等,謝謝。
          2010-05-13 16:41 | Lancelot

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          額 我想你們理解錯我的意思了

          10^n 我表示的是10的n次方

          不好意思啊
          2010-05-13 17:24 | Zhjiang

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          各位如果要有更好的算法,把代碼貼出來,不要光說一些理論。
          2010-05-13 18:28 | 銀河使者

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          什么都不說 粗略寫的java 代碼 大家研究下

          import java.io.BufferedReader;
          import java.io.IOException;
          import java.io.InputStreamReader;


          public class Test {

          /**
          * @param args
          */
          public static void main(String[] args)
          {
          BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
          String numStr =null;
          long num = -1;
          long tempnum = 0;
          long bitvalue = -1;
          int numbit1 = -1;
          int numbit2 = -1;
          int flag = -1;
          int size = 0;
          boolean isrepeater = false;

          try
          {
          numStr = br.readLine();
          num = Long.parseLong(numStr);

          } catch (NumberFormatException e)
          {
          return;
          } catch (IOException e)
          {
          return;
          }
          //只解析自然數
          if(num <= -1)
          return;

          size = (int)Math.log10(num);

          //1. 先將數字+1
          num ++;
          tempnum = num;

          //2. 判斷是否為重復數字
          if(tempnum / Math.pow(10, size) >= 1)
          size ++;

          bitvalue = (long)Math.pow(10, size - 1);

          for(flag = size - 1; flag >= 1; flag --){

          numbit1 = (int)(tempnum / bitvalue);

          tempnum = tempnum % bitvalue;

          bitvalue = (long)Math.pow(10, flag - 1);
          numbit2 = (int)(tempnum / bitvalue);

          if(numbit1 == numbit2){
          isrepeater = true;
          break;
          }
          }
          //2. 如果是重復數
          if(isrepeater){
          flag --;
          num -= tempnum % Math.pow(10, flag);

          //以下三行是由于考慮的遺漏,解決了以99開頭數字(例 9998)的問題
          //按照我以前的算法99123 得到的結果是100010
          num += (long)Math.pow(10, flag);
          if(Math.log10(num) >= size)
          num += (long)Math.pow(10, flag);

          flag -= 2;
          for(; flag >= 0; flag -=2){
          num += (long)Math.pow(10, flag);
          }
          }

          //4.輸出結果
          System.out.println(num);

          }

          }
          2010-05-13 18:30 | Zhjiang

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          我也只是剛剛寫的,遺漏之處和我說一下,謝謝了!
          在我看來這個題目純粹是一個計算的問題,不需要過多的循環的,當然 僅爭對本題,另外解釋一點,我上面的代碼沒有把負數考慮進去,只要稍微改進一下就可以把負數也包括進去,當然這點還需要商榷,我只是一個猜想,因為準備寫代碼的時候已經下班了,不想在公司繼續熬了,所以代碼寫得很粗糙,沒有優化,另外也沒有過多檢查BUG和遺漏的情況,往諒解下!
          2010-05-13 18:34 | Zhjiang

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          @Lancelot
          哈哈,上機測一下程序不就知道了。你不知道,可計算機知道哦。
          2010-05-14 08:23 | 銀河使者

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          看到你在Csdn上寫的,看來不止是我覺得擴展01序列是一個不錯的方法!~~
          很慶幸
          2010-05-14 09:10 | Zhjiang

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          @銀河使者
          拜托你告訴我你用的什么計算機???
          你的計算機會把10^7算成3還是10000000???
          搞笑


          我都懶的理你了,這是最后一次回復你。
          2010-05-17 17:23 | Lancelot

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          @Lancelot
          什么意思,我的算法沒10^7,估計是你看到別人的回復算到我頭上了吧。如果認為哪個具體的數無法通過算法,請指出,謝謝。

          對了,忘了說了,上星期我剛從某個外星種族那得到一臺先進的計算機,好象10^7既不等于3,也不等于10000000,等于^&%$&*^&*。 :)
          2010-05-17 19:33 | 銀河使者

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          Zhjiang的算法思路不錯, 不過還是有一些缺陷的.對于中間含有多個9的數字,處理不對。如99,1990,999...
          2010-05-17 23:42 | 曉風待語

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          本人blog中有對該題的詳細解法,希望給予意見
          2010-05-17 23:45 | 曉風待語

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數[未登錄]  回復  更多評論   

          確實覺得從左至右是合理的,
          比如說一個數字,567899882201
          首先+1,然后執行如下步驟:
          步驟一:從左至右查找第一對重復的是99,操作:(567899 + 1)*10^6 =
          567900 000000
          其實這樣的操作會使任何一個數字都帶有0到n個0。如55 的話
          (55+1)*10^0 ,78662為:(7866+1)*10^1;
          步驟二:從左至右查找第一對重復的數字,如果重復的數字為0,即為00,執
          行步驟三,否則執行步驟一;
          步驟三:從左至右查找連續的兩個數字,此時連續的兩個數字只有可能是00了,
          如果有兩個連續的0,這時按照00+1,如:(567900+1)*10^6;

          只是寫了一個大致的實現思路,這個只是針對>=0的數字,^的意思是Math.pow(10, n);只是一個表現形式,大家也不用太糾結
          2010-06-25 10:56 | lj

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數[未登錄]  回復  更多評論   

          假如上個月(5月)總銷售額是39980,而本月(6月)總銷售額是59863,問:本月銷售同比上月銷售增長多少百分比,反比增長多少百分比?
          2010-07-03 13:35 | 小李

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          看了一下大伙的討論 覺得蠻有意思
          轉走了
          http://www.wangdacuo.com/archives/solve-question-use-java
          2010-07-13 19:26 | 特立獨行

          # re: 有道難題2009復賽題解答(Java版):求大于給定數的最小不重復數  回復  更多評論   

          我感覺這題好簡單啊……
          1.從高到低位掃描,遇到與高一位數相同的數,就在該位置上加1,把該為后的全置0,
          2.重復1直到你掃描到個位且它與十位不同
          就搞定了……
          2011-04-28 22:41 | 喬磊
          主站蜘蛛池模板: 牡丹江市| 长子县| 金沙县| 西和县| 揭西县| 临海市| 洪湖市| 安图县| 九江县| 临沭县| 获嘉县| 德化县| 兖州市| 西峡县| 丽水市| 道真| 南溪县| 大丰市| 巴里| 都兰县| 宜州市| 喀什市| 衡阳县| 仁怀市| 通州区| 南安市| 定南县| 万州区| 资讯 | 珠海市| 广昌县| 东源县| 宜州市| 涟源市| 蓝山县| 屏南县| 沂南县| 六安市| 瑞安市| 湟中县| 金塔县|