有道难题2009复赛题解答(Java版):求大于给定数的最小不重复数

有道难题2009复赛题解答(Java版):求大于给定数的最小不重复数

分类: java algorithm   2364人阅读  评论(34)  收藏  举报
java web开发 新浪微博 算法 测试 android

本文为原创,如需转载,请注明作者和出处,谢谢!

最近看了有道出的几个复赛题,觉得很好玩,现给出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手机客户端(新浪微博) 发布 

《银 河系列原创教程》 发布 

《Java Web开发速学宝典》 出版, 欢迎定购

查看评论
26楼  phh1989 2011-10-08 09:16发表 [回复]
左边开始查找相同的,找到相同的之后后面的改为010101.....
25楼  phh1989 2011-10-08 09:14发表 [回复]
引用“Tsiannian”的评论:回复Tsiannian:算法复杂度 1
不错。。
24楼  Tsiannian 2011-10-01 23:14发表 [回复]
直接查找最高的重复位修改,后面变为0101……
Re:  Tsiannian 2011-10-01 23:16发表 [回复]
回复Tsiannian:算法复杂度 1
23楼  zkgale 2011-07-08 16:55发表 [回复]
[java]  view plain copy
  1. package org.juqkai.youDao;  
  2.   
  3.   
  4. public class Shu{  
  5.     char v;  
  6.     Shu prev;  
  7.     Shu next;  
  8.     public Shu(char c){  
  9.         v = c;  
  10.     }  
  11.     void initZero(){  
  12.         v = '0';  
  13.         if(next != null){  
  14.             next.initZero();  
  15.         }  
  16.     }  
  17.     void plus(){  
  18.         if(v != '9'){  
  19.             v += 1;  
  20.             check();  
  21.             return;  
  22.         }  
  23.         initZero();  
  24.         if(prev != null){  
  25.             prev.plus();  
  26.         } else {  
  27.             prev = new Shu('1');  
  28.             check();  
  29.         }  
  30.     }  
  31.     void check(){  
  32.         if(prev == null || prev.v != v){  
  33.             if(next != null)  
  34.                 next.check();  
  35.         }else {  
  36.             plus();  
  37.         }  
  38.     }  
  39.     public String toString() {  
  40.         if(next != null)  
  41.             return v + next.toString();  
  42.         return String.valueOf(v);  
  43.     }  
  44. }  
22楼  zkgale 2011-07-08 16:55发表 [回复]
这是我的实现

[java]  view plain copy
  1. /** 
  2.  * 连续数 
  3.  * 如果一个数字十进制表达时,不存在连续两位相同,则称之为“不重复数”。 
  4.  * 例如,105、1234和12121都是“不重复数”,而11、100和1225不是。 
  5.  * 给定一个long long类型数字A,返回大于A的最小“不重复数”。 
  6.  * @author Administrator 
  7.  * 
  8.  */  
  9. public class UnrepeatingNumbers {  
  10.     long getNext(long a){  
  11.         Shu s = make((String.valueOf(a + 1)).toCharArray());  
  12.         s.check();  
  13.         String v = s.toString();  
  14.         return Long.parseLong(v);  
  15.     }  
  16.       
  17.     public static Shu make(char[] s){  
  18.         Shu tem = null;  
  19.         for(int i = s.length - 1; i >= 0; i --){  
  20.             Shu k = new Shu(s[i]);  
  21.             if(tem == null){  
  22.                 tem = k;  
  23.                 continue;  
  24.             }  
  25.             k.next = tem;  
  26.             tem.prev = k;  
  27.             tem = k;  
  28.         }  
  29.         return tem;  
  30.     }  
  31.       
  32.     public static void main(String []args){  
  33.         UnrepeatingNumbers un = new UnrepeatingNumbers();  
  34.         long x = un.getNext(210990000000000000l);  
  35. //      long x = un.getNext(21099);  
  36. //      long x = un.getNext(54);  
  37.         System.out.println(x);  
  38.     }  
  39. }  
21楼  Marryday 2010-07-13 14:22发表 [回复]
是我看错啦[e06]
20楼  Marryday 2010-07-13 14:15发表 [回复]
这道题我感觉只能从左边开始判断, 举个例子:
如1000, 从右起 第一步 1000-&gt;1001-&gt;1011-&gt;1012
从左起1000-1010; 你们想过么?也许是我理解错了吧
19楼  lyfhqb2008 2010-05-17 13:26发表 [回复]
[e01]
18楼  yewen0125 2010-05-14 15:49发表 [回复]
你的思想不错, 但代码还有待改进。 不用算法, 截字符串是不好的, 违背了算法的初衷。
17楼  geekguy 2010-05-14 13:03发表 [回复]
这个题目前的思路都差不多,谁有更创新的方法可以贴出来
16楼  EricChan1986 2010-05-14 11:57发表 [回复]
话说你看到这个题目的地方解题思路应该都已经给出来了的。。。
15楼  Cody_Yu 2010-05-14 08:41发表 [回复]
是不是中间出现99的话,后面都会变成010101这样的啊
14楼  macrojj 2010-05-13 20:37发表 [回复]
按这个算法 20551 
先加1 -&gt;20552
然后 找到最右边的一对重复的 55 +1 -&gt;56
20562

但是我觉得应该是20561 最小吧。。。
Re:  macrojj 2010-05-13 20:44发表 [回复]
回复 macrojj:不好意思 我理解错了
Re:  geekguy 2010-05-14 08:20发表 [回复]
回复 macrojj:
比20551大的最小不重复数是20560
13楼  geekguy 2010-05-13 20:05发表 [回复]
这个题里面不会有太多的计算,因此,是对10取整、取余,或转换成字符串的效率着不多。当然,如果要将本方法用于另个的大循环中,当然是对除10效率更高一些。本题只是一个思路,改进的地方还很多。我并没做任何的优化。只是简单地用代码描述下算法。如果哪位有更优化的代码,可以跟贴,互相学习。[e04]
12楼  Morecans 2010-05-13 19:45发表 [回复]
可以考虑如下算法
long n;
n=+1;
int position= 1;
while((position=lookupDup(n, position))!=0){
processDup(n, position);
position++;
}

lookupDup(n, position)没有重复数返回0,否则返回从右往左的重复数的低位数字的位置,且从position开始。个位位置为1即position= 2,十位数字位置为2即position= 2;

processDup(n, position)处理重复数使position 与position+1上的数字不重复。直接直接将position 位加一,知道不重复,最多加两次就不会重复了。
11楼  siao 2010-05-13 16:42发表 [回复]
我觉得应该有改进的地方,递归里面我肯定不会把一个数变成了字符串来判断,直接判断/10前后个位数一不一样就可以
10楼  匿名用户 2010-05-13 15:49发表 [回复]
不错学习了![e03]如哪位兄弟所说,如果从左边开始找重复的话,应该会有问题,比如是29909831,进位的问题,怎么思考呢?
Re:  匿名用户 2010-05-13 16:32发表 [回复]
回复 匿名用户:也就是299变成300,然后对300进行递归处理,300变成301,递归完成右边写上01010,结果就是30101010
9楼  匿名用户 2010-05-13 13:33发表 [回复]
应该是从右向左寻找呵呵
8楼  匿名用户 2010-05-13 13:30发表 [回复]
把每一位拆开存储在数组中,从左向右寻找第一个重复的数,在后一个重复的数+1,判断是否导致进位,如果进位向前回退,一直退到不在进位处,从此处看是判断左右两侧是否重复数,依次向后。
7楼  匿名用户 2010-05-13 12:49发表 [回复]
这个不错,研究起来挺有意思的。[e03][e01]
6楼  xjoooo 2010-05-13 11:34发表 [回复]
[e03]
5楼  nova_xmu 2010-05-13 09:58发表 [回复]
http://www.youdao.com/nanti/news.html 仔细看看解题报告。。。
4楼  匿名用户 2010-05-13 09:44发表 [回复]
回复 匿名用户:对于1099,先加1变成1100。从左找第一个重复位,即第二位,于是从第二位把这个数拆成11和00两半。对于左半部分的11,加1变成12,再对12进行递归分析,发现无重复,递归终止。然后因为该位置最终结果不是1,把右边部分按照0101的模式变成01,结果就是1201。算上最初的一次加1,总共只进行了2次加1操作。而从右边开始加,得进行4次。
Re:  yl_zwn 2010-05-14 16:13发表 [回复]
回复 匿名用户:对就是这样,要用到函数的递归调用哈。
3楼  匿名用户 2010-05-13 00:02发表 [回复]
与其从右边开始找重复对,不如从左边开始找,效率更高。找到之后,在当前重复位置加1,并对左边部分进行递归。然后视当前位的结果,把右边变成0101或者1010就可以了。
Re:  yl_zwn 2010-05-14 16:12发表 [回复]
回复 匿名用户:这个想法太好了,我已经按这个想法编码实现了哈。
Re:  匿名用户 2010-05-13 09:16发表 [回复]
回复 匿名用户:你实践一下再说。我想你考虑的不周全。例如是1099呢,如果你从左到右有什么问题!?你还得往回看看进位!
古人的数学是从右往左进位的,如果你想来点特殊的可以。
Re:  llh110220 2011-09-23 16:58发表 [回复]
引用“匿名用户”的评论:回复 匿名用户:你实践一下再说。我想你考虑的不周全。例如是1099呢,如果你从左到右有什么问题!?你...
回复匿名用户:如果你不小心,那么
那么得到的数据往往不是最小的的
2楼  xiaopohair 2010-05-12 19:55发表 [回复]
[e03]
1楼  匿名用户 2010-05-12 18:40发表 [回复]
[e01]

你可能感兴趣的:(java,Algorithm)