寻找最大的非重复数

好像好久都没在编程栏目下投新稿了(

那就来篇水点的,顺便测试下代码高亮插件

报了华为编程大赛,找了去年的题,看到了这道:

如果一个数字十进制表达时,不存在连续两位相同,则称之为“不重复数”。
例如,105、1234和12121都是“不重复数”,而11、100和1225不是。
给定一个正整数A,返回大于A的最小“不重复数”。A小于100000

初见一看,从所给之数开始依次加一,判断新的数是否是重复数就行了,可谓是最笨而又保险的做法

很快写完后,恰好有友人过来一同讨论,是否有更优的算法呢?

 

先从这个每步+1着手,诸如122421这样的数,千位和万位重复,再从个位加真是太累了;于是改成从高位向低位遍历,遇到重复的邻近位就把低位+1,至少效率会比+1好多了;

对122421采用这种做法,会+1000变成123421,看上去的确是不重复了,但却丢失了最小性(最小的应该是123010)

因此,在修改重复位之后,还需要把低位重置,重置成0、1相邻的序列,就能弥补最小性了

例如122121,先+1000变为123121,再从123后面填入序列,得到123010;

例如120014,先+100变为120114,再从1201后面填入序列,得到120101

 

在这种做法下,循环数量可谓是大大减少了,很多情况下更是一步到位;但是依然没有摆脱“自增修改->判断是否为重复数”的循环试探法

要得到一步到位的稳定法,还需考虑进位的情况:

例如1219922,在遍历到9并+100后,变为1220022,会对已遍历过的高位引入新的重复;此时再将遍历位回退2位重新遍历,即能解决进位带来的不稳定情况

 

总结一下,最终一步到位的算法步骤为:

1.将原数+1得到目标数字

2.对目标数字从高位向低位遍历,若未发现邻近重复数字,则转入步骤4;否则将2个重复数字中的低位数字+1,并记录重复处之后的低位位数

3.遍历位置回退2,重复步骤2

4.若记录了重复处之后的低位位数,则将这些低位用0、1相间的序列进行填充;返回修改后的目标数字

 

顺便附上输入数据为整形时的代码(c++字符串的代码有点长就不贴了):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned int GetNotRepeatNum(unsigned int lValue){
    int temp, aim = lValue + 1;
    int divide = pow(10.0f, int(log10(double(aim))) - 1);
    while (divide >= 1){
        temp = (aim / divide) % 10;
        if (temp == (aim / (divide * 10)) % 10){
            aim = (aim / divide + 1) * divide;
            if (temp == 9){
                divide *= 100;
            }
        }else{
            divide /= 10;
        }
    }
    return aim;
}

最后发现自己实在是太罗嗦了,其实本篇就是一道可以用一句话来概括的水题,“高位到低位扫描,遇到相邻位相同,低位+1,低位后面的全置为0;重复上述直至十位与个位不同”,当然也可以用三个字,“构造法”

最近一段时间也是老夫聊发少年狂,刷了些oj,弥补以前的遗憾;不过也只能从实效性下手,无暇追求排名与AC数了。新手上道,挫折无限,还需多多修炼

3 条评论

  1. hongbai 说道:

    教主你lvalue<100000判断没加0.0

发表评论

电子邮件地址不会被公开。

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>