将字符串中表示的大数除以2,加1或减1

时间:2016-09-27 21:59:00

标签: algorithm

我现在正致力于代码挑战。我的解决方案得到了“时间超过”,即使我已经优化了它。我正在寻求更有效的解决方案的帮助或更多地优化我的解决方案。

问题的描述是: 编写一个函数,它将一个正整数作为一个字符串,并返回将数字转换为1所需的最小操作数。这个数字长达309位,所以没有太多的字符可以表达多少数字。 转换过程仅限于三个操作: 1.加1 2.减去1 3.将数字除以2(此处仅允许偶数)

我的想法是使用DFS通过记忆来遍历所有可能的解决方案以加快速度。但它确实超过了时间限制。问题不能使用dp,因为dp需要一个非常大的数组来记忆。以下是我的代码:

private static int dfs(String num, int step,Map<String,Integer> memory){
        if(num.equals("1")){
            return step;
        }
        Integer size = memory.get(num);
        if(size != null && size < step){
            return Integer.MAX_VALUE;
        }
        memory.put(num, step);
        int min = Integer.MAX_VALUE;
        int lastDigit = num.charAt(num.length() - 1) - '0';
        if(lastDigit % 2 == 0){
            min = Math.min(min, dfs(divideBy2(num), step + 1, memory));
        }else{
            min = Math.min(min, dfs(divideBy2(num), step + 2, memory));
            min = Math.min(min, dfs(divideBy2(plusOne(num)), step + 2, memory));
        }
        return min;
    }
    private static String plusOne(String num){
        StringBuilder sb = new StringBuilder();
        int carry = 1;
        for(int i = num.length() - 1; i >=0; i--){
            int d = (carry + num.charAt(i) - '0') % 10;
            carry = (carry + num.charAt(i) - '0') / 10;
            sb.insert(0, d);
        }
        if(carry == 1){
            sb.insert(0, carry);
        }
        return sb.toString();
    }
    private static String divideBy2(String num){
        StringBuilder sb = new StringBuilder();
        int x = 0;
        for(int i = 0; i < num.length(); i++){
            int d = (x * 10 + num.charAt(i) - '0') / 2 ;
            x = (num.charAt(i) - '0') % 2 ;
            if( i > 0 || (i == 0 && d != 0))
                sb.append(d);
        }

        return sb.toString();
    }

注意:经过几个案例的测试:我有一些意义,但不能概括规则。 如果当前的数字是奇数。我们在这里有两个选择:加1或减1.操作后的数字可以再划分2次,步骤会更短。

更新:嗨,伙计们,我整晚都在工作,并找到通过测试的解决方案。这个想法是将问题分成两个子问题:1。如果数字是偶数,则将其除以2。 2.如果数字是奇数,选择让数字在其位表示中有更多拖尾零的方式。我将更多地解释奇怪的情况:如果数字是奇数,最后两位可以是“01”或“11”。当它为“01”时,将其减1,使最后两位变为“00”。如果是“11”,则将其增加1,生成“00”。通过这样做,由奇数生成的下一个偶数可以被分割更多次,这在实践中确实很快。下面是我的代码,如果您对实现有一些疑问,请随时给我发消息:

public static int answer(String n) { 

        // Your code goes here.
        int count = 0;
        while(!n.equals("1")){
            if((n.charAt(n.length() - 1) - '0') % 2 == 0){
                n = divideBy2(n);
            }else if(n.equals("3") || lastTwoBit(n)){
                n = subtractOne(n);
            }else{
                n = plusOne(n);
            }
            count++;
        }
        return count;
    } 
      private static boolean lastTwoBit(String num){
          int n = -1;
          if(num.length() == 1){
              n = Integer.valueOf(num);
          }else{
              n = Integer.valueOf(num.substring(num.length() - 2, num.length()));
          }
          if(((n >>> 1) & 1) == 0){
            return true;
          }
          return false;
      }
      private static String subtractOne(String num){
         if(num.equals("1")){
            return "0";
         }
         StringBuilder sb = new StringBuilder();
         int carry = -1;
         for(int i = num.length() - 1; i >= 0; i--){
             int d = carry + num.charAt(i) - '0';
             if(d < 0){
                 carry = -1;
                 sb.insert(0, '9');
             }else if((d == 0 && i != 0) || d > 0){
                 carry = 0;
                 sb.insert(0, d );
             }
         }
         return sb.toString();
     }
    private static String plusOne(String num){
        StringBuilder sb = new StringBuilder();
        int carry = 1;
        int i = 0;
        for(i = num.length() - 1; i >=0; i--){
            if(carry == 0){
                break;
            }
            int d = (carry + num.charAt(i) - '0') % 10;
            carry = (carry + num.charAt(i) - '0') / 10;
            sb.insert(0, d);
        }
        if(carry ==0){
            sb.insert(0, num.substring(0, i + 1));
        }
        if(carry == 1){
            sb.insert(0, carry);
        }
        return sb.toString();
    }
    private static String divideBy2(String num){
        StringBuilder sb = new StringBuilder();
        int x = 0;
        for(int i = 0; i < num.length(); i++){
            int d = (x * 10 + num.charAt(i) - '0') / 2 ;
            x = (num.charAt(i) - '0') % 2 ;
            if( i > 0 || (i == 0 && d != 0))
                sb.append(d);
        }

        return sb.toString();
    }

1 个答案:

答案 0 :(得分:-2)

虽然不是1 ...   如果奇数...减去1 =&gt;甚至   如果偶数......除以2。

只需对操作进行求和并返回。

e.g。 5593
5593 -1 = 5592 /2 = 2796 /2 = 1398 /2 = 699 -1 = 698 /2 = 349 -1 = 348 /2 = 174 /2 = 87 -1 = 86 /2 = 43 -1 = 42 /2 = 21 -1 = 20 /2 = 10 /2 = 5 -1 = 4 /2 = 2 /2 = 1

19 Operations -///-/-//-/-/-//-//

编辑:时间复杂度为O(logN),我们将数字除以2 /减去然后除。 和空格为O(1)

    public int make1(string s)
    {
        int n = 0;
        while(s != "1")
        {
            switch(s[s.Length-1])
            {
                case '0':
                case '2':
                case '4':
                case '6':
                case '8':
                    s = div2(s);
                    ++n;
                    break;
                case '1':
                case '3':
                case '5':
                case '7':
                case '9':
                    s = minus1(s);
                    s = div2(s);
                    n += 2;
            }
        }
        return n;
    }