【问题标题】:How to get the least number after deleting k digits from the input number从输入数字中删除k位后如何获得最少的数字
【发布时间】:2015-01-29 20:03:47
【问题描述】:

例如输入号码为24635,删除任意3位后最少为23

这和取最小的两个数字是不一样的,因为必须保持数字的顺序。

【问题讨论】:

  • 23 是一个不错的数字,但我完全不明白。我希望是 24 或 35,这取决于你是从前面还是后面消除数字。
  • @AdityaGoel 您是否建议删除 3 个 chars? (24) 正确筛选 3 次? (3079) 删除 3 个十六进制数字? (6)
  • 您的意思是要保留两个最低位吗?或者更确切地说,删除 X 个 最高 位。如果你这样做了,那么这里有一个提示:如果你对数字集合进行排序会更简单。
  • @JoachimPileborg,这与保留最低两位数不同。十进制形式的最终数字必须与原始数字的顺序相同。例如在43331 中,我们可以有41,但不能有14。因此31 是最小的可能 - 14 是不可实现的
  • 我刚刚编辑了问题中的示例,但我有点后悔,因为阅读基于旧数字的讨论会令人困惑?我可以“撤消”我的编辑吗?

标签: c algorithm data-structures


【解决方案1】:

删除k 位数意味着保留n - k 位数,其中n 是总位数。

使用您保持升序排序的堆栈。只要您仍然可以使其达到n - k 数字并且您当前的元素小于堆栈的顶部,您就可以从中删除元素:

push(2) => 2
push(4) because 2 < 4 => 24
push(6) because 4 < 6 => 246
pop() because 3 < 6 and we can still end up with 2 digits => 24
pop() for the same reason => 2
push(3) => 23
push(5) => 235

然后只取第一个 k 数字 => 23。或者您可以确保推送的数字永远不会超过k,然后最终的堆栈就是您的解决方案。

请注意,如果这意味着您将无法构建k 数字的解决方案,则您不能弹出元素。为此,您需要检查堆栈中当前元素的数量以及输入数字中当前位置右侧的位数。

伪代码:

stack = []
for each d in digits:
  while !stack.empty() and d < stack.top() and (*):
    stack.pop()

  if stack.size() < n - k:
    stack.push(d)

 (*) - exercise, fill in the condition that prevents you 
       from popping elements if you still want to be able to get to a solution.
 Hint: count how many elements the for loop went over already
       and see how many are left. Also consider how many you have left in the stack.

由于每个元素最多进出栈一次,复杂度为O(n)

【讨论】:

  • 您的复杂性分析是错误的。 O(n) 意味着每个元素只被考虑一次。对于输入中的任何数字,可能必须将其与 所有 以前的数字(在插入时已进行比较)进行比较。
  • @JonathanMee - 不,实际上是正确的。首先,O(n) 并不意味着每个元素只被考虑一次,它意味着每个元素只被考虑恒定的次数。就我而言,这个常数大约是2。对于输入中的任何数字,它只会与堆栈中的元素进行比较。事实上,我们可能会比较堆栈中的所有元素,但之后我们也会清空堆栈,因此它减少了未来的工作。查看堆栈中每个元素被访问(插入/删除)的次数,您会看到它是O(n)
  • 你错了,这个算法需要一个嵌套的for循环。在我看来 O(NlogN)。
  • @JonathanMee - 是的,它会的,或者嵌套在for 中的while。这并没有说明复杂性,也不是什么都可以放弃。更多的嵌套循环并不总是等于更高的复杂性。如果你不相信我,你可以实现它,看看一百万位数需要多长时间,或者你可以提出一个关于它的新问题。绝对是O(n),对于我在上一条评论中概述的论点。
  • 就像一般公告一样,IVlad 是正确的大 O 符号。在最坏的情况下,该算法执行 2n-1 次比较,因此是 O(n) 时间。
【解决方案2】:

提供递归方法。

在每次迭代测试成功k == 0 ...
或失败num == 0,因为没有数字可以删除。
(返回 10 比返回 num 的其他路径更糟糕。)

否则以两种方式递归:
1) 保留最低有效位并尝试使用高位数字。
2) 删除最低有效位并尝试使用高位,k--
退回更好的。

unsigned reduce(unsigned num, unsigned k) {
  if (k <= 0) {
    return num;  // Success
  }
  if (num == 0) {
    return 10;  // Fail
  }
  unsigned path1 = reduce(num/10, k)*10 + num%10;
  unsigned path2 = reduce(num/10, k-1);
  return path1 < path2 ? path1 : path2;
}

int main(void) {
  printf("%u\n", reduce(246, 2));
  printf("%u\n", reduce(24635, 3));
  printf("%u\n", reduce(53642, 3));
  printf("%u\n", reduce(21, 1));
}

2
23
32
1

此解决方案不依赖于知道位数,只依赖于需要删除的数字。

【讨论】:

  • 这里的复杂性可能是最糟糕的,但我无法相信这么简单的事情就能解决问题+1。
【解决方案3】:

非常简单的解决方案。

我们有n 数字,我们必须删除其中的k 才能留下n-k。我们可以很容易地确定第一个数字是什么。

最后的n-k-1 数字显然不能是答案的第一位,后面的数字不够多,无法组成足够长的数字。

因此,我们简单地忽略最后的 n-k 数字,并专注于第一个 k 数字,并在该 k 数字集合中找到最小的数字。这将是我们答案的第一个数字。例如无论 X 是什么,所有 5XX 的三位数字都小于 6XX 形式的所有数字。 (如果出现平局,两位数共享最小的荣誉,那么选择左边的数字,因为它可以让您以后更灵活。)

现在您知道第一个数字是什么,您可以忽略它以及左边的所有内容,并使用剩余的数字递归地重复整个过程 - 我可以从剩余的数字中得出的最小数字是多少?

【讨论】:

    【解决方案4】:

    要解决这个问题,您可以按照以下步骤操作 -

    • 定义一个栈st,创建一个空字符串ret

    • n := num 的大小

    • 对于 i 在 0 到 n – 1 的范围内

    • 当 k 不为零且堆栈不为空且堆栈顶部时 > num[i]

    • 从堆栈中删除并将 k 减 1

    • 将 num[i] 插入 st

    • 当k不为0时,从栈中删除元素

    • 当堆栈不为空时

    • ret := ret + 栈顶,从栈中删除元素

    • 现在反转 ret 字符串

    • ans := 一个空字符串,并且 i := 0

    • 当 i

    • 将 i 增加 1

    • for i

    • ans := ans + ret[i]

    • ret := ans

    • 如果 ret 的大小为 0,则返回“0”,否则为 ret

    C++ 解决方案:

    class Solution {
    public:
       string removeKdigits(string num, int k) {
          stack st;
          string ret = "";
          int n = num.size();
          for(int i = 0; i < n; i++){
             while(k && !st.empty() && st.top() > num[i]){
                st.pop();
                k--;
             }
             st.push(num[i]);
          }
          while(k--)st.pop();
          while(!st.empty()){
             ret += st.top();
             st.pop();
          }
          reverse(ret.begin(), ret.end());
          string ans = "";
          int i = 0;
          while(i <ret.size() && ret[i] == '0')i++;
          for(; i < ret.size(); i++)ans += ret[i];
          ret = ans;
          return ret.size() == 0? "0" : ret;
       }
    };
    

    【讨论】:

      【解决方案5】:

      我想到了一个非常简单的算法

      1. 将输入数字视为字符串
      2. 开始从数字 9 到 0 的反向循环
      3. 检查给定数字中是否存在9(在java中我们可以用indexOf()来完成),如果存在则删除。检查删除的位数是否等于 b 已删除的输入位数。如果不是,则检查 9 是否存在直到它不存在(indexOf() 在 java 下将返回 -1)以查看是否重复相同的数字。现在重复如果 8 直到预期没有数字被删除
      4. 到目前为止,我们已经删除了可能导致更大数字的数字
      5. 现在从 0 循环到 9 并检查 number 中存在的每个数字是否在步骤 3 中得到并按升序重新排列

      例如:-

      给定号码是24635,要删除的数字是2

      1. 从 9 开始,您发现要删除的第一个数字是 6,然后是 5
      2. 剩余数量为 243
      3. 从 0 到 9 开始循环按升序重新排列,如 234

      【讨论】:

        【解决方案6】:

        我认为这样可以解决问题:

        让我们使用 532874902352。如果您愿意,可以随意尝试。

        对于 532874902352,我们将删除 4 位数字,或者您想要的任何数字。

        移动 4+1 位数字。53287|4902352

        找出最小的数。 2

        删除所选数字之前的所有数字。我们现在有 28749023532;我们删除了 2 位数字。还有 2 个。

        删除第一个数字后的 2 个数字。我们有 249023532。开始吧。

        但是,如果要删除的数字是倒数第二个,如果它大于倒数第二个,则删除最后一个。

        例子:

        24635,删除 3。

        3+1 入住。

        2463|5

        删除2之前的所有,最小的。

        24635

        删除以满足所需的位数,3。 但删除 5 而不是 3。

        23

        43331,删除 3。

        3+1 入住。

        4333|1

        删除3之前的所有,最小的。

        31

        删除以满足所需的位数,3。 我们没有要删除的数字了。

        由你来实现这个方法。

        【讨论】:

          【解决方案7】:

          知道你想保持数字顺序肯定会让这个解决方案变得更加困难。

          我同意IVlad 的观点,即char[]s 不是正确的选择。

          我认为这是一个相当不错的解决方案:

          #include <stdio.h>
          #include <math.h>
          #include <limits.h>
          
          #define DIGITS_TO_REMOVE 3 // Assumed to be positive
          
          int recurse(int* foo, int begin, int end, int previous, int max){
              int i;
              int min = begin;
          
              for (i = begin; i <= end; ++i){
                  if (foo[min] > foo[i]){
                      min = i;
                  }
              }
          
              return previous * pow(10, max - end + 1) + (max > end ? recurse(foo, min + 1, end + 1, foo[min], max) : foo[min]);
          }
          
          int main(void) {
              int foo[(const int)ceil(log10(INT_MAX))];
              int bar = 24635; // Assumed to be larger than pow(10, DIGITS_TO_REMOVE) - 1
              int size = ceil(log10(bar));
              int i;
              int min = size - DIGITS_TO_REMOVE;
          
              for (i = 1; bar > 0; bar /= 10, ++i){
                  foo[size - i] = bar % 10;
          
                  if (i >= DIGITS_TO_REMOVE && foo[size - i] <= foo[min]){
                      min = size - i;
                  }
              }
          
              printf("%d", recurse(foo, min + 1, DIGITS_TO_REMOVE + 1, foo[min], size - 1));
              return 0;
          }
          

          编辑:

          IVlad 还建议我让解决方案返回一个数组,而不仅仅是一个 int,这样它就不会受到返回类型的大小的限制。显然,准备输入和输出数组需要做一些工作,因此这可能不是 OP 的目标,但这是一个有趣的问题。

          #include <stdio.h>
          
          #define DIGITS_TO_REMOVE 3 // Assumed to be positive
          #define INPUT_SIZE 5 // Assumed to be greater than DIGITS_TO_REMOVE
          
          void recurse(int* input, int* output, int begin, int end){
              int i;
              int min = begin;
          
              for (i = begin; i < end; ++i){
                  if (input[min] > input[i]){
                      min = i;
                  }
              }
              output[end - DIGITS_TO_REMOVE - 1] = input[min];
          
              if (end < INPUT_SIZE){
                  recurse(input, output, min + 1, end + 1);
              }
          }
          
          int main(void) {
              int foo[] = { 2, 4, 6, 3, 5 };
              int bar[INPUT_SIZE - DIGITS_TO_REMOVE];
              int i;
          
              recurse(foo, bar, 0, DIGITS_TO_REMOVE + 1);
          
              for (int i = 0; i < INPUT_SIZE - DIGITS_TO_REMOVE; ++i){
                  printf("%d", bar[i]);
              }
              return 0;
          }
          

          【讨论】:

          • 显然你必须写很多才能解释为什么你给了一个-1,所以我尽可能正式地写这个。我投了反对票,因为这不能解决 OP 的问题。
          • @IVlad 是的,我确实误解了第一遍的问题。编辑为我清除了它。
          • 您正在使用 sprintf、对数和 qsort 来解决一个不需要任何这些的简单问题。而且您的解决方案仅适用于适合 int 的数字。抱歉,但我不认为是我们在这里试图重新发明轮子......
          • @IVlad 嗯...你认为用数字来做会更清楚吗?我会试试看...
          • 我认为找出问题比找出答案更难。让我想起了一些事情……;-)。
          【解决方案8】:
          private static int getLeastNumberAfterDelete(int num, int dighitsDel) {
           String s = "";
           char[] charArray = String.valueOf(num).toCharArray();
           Arrays.sort(charArray); // Sort charArray
           for (int i = 0; i < (charArray.length - dighitsDel); i++) {
           s += charArray[i];//concatenate 
           }
           return Integer.valueOf(s);
           }
          

          【讨论】:

            【解决方案9】:

            这个想法是基于这样一个事实,即前 (n+1) 个字符中的一个字符必须在结果数中。因此,我们选择前 (n+1) 个数字中最小的一个并将其放入结果中,并为剩余的字符重复出现。下面是完整的算法。

            将结果初始化为空字符串,即 res = ""

            buildLowestNumber(str, n, res)

            1) 如果 n == 0,则无需删除任何内容。 将整个 'str' 附加到 'res' 并返回

            2) 设 'len' 为 'str' 的长度。如果 'len' 小于或等于 n,然后一切都可以删除。不附加任何内容到“res”和 返回

            3) 找出前 (n+1) 个字符中最小的字符 'str'。设最小字符的索引为 minIndex。附加 'str[minIndex]' 到 'res' 并在 minIndex 和 对于 n = n-minIndex

            buildLowestNumber(str[minIndex+1..len-1], n-minIndex).

            #include<iostream>
            using namespace std;
            
            // A recursive function that removes 'n' characters from 'str'
            // to store the smallest possible number in 'res'
            void buildLowestNumberRec(string str, int n, string &res)
            {
                // If there are 0 characters to remove from str,
                // append everything to result
                if (n == 0)
                {
                    res.append(str);
                    return;
                }
            
                int len = str.length();
            
                // If there are more characters to remove than string
                // length, then append nothing to result
                if (len <= n)
                    return;
            
                // Find the smallest character among first (n+1) characters
                // of str.
                int minIndex = 0;
                for (int i = 1; i<=n ; i++)
                    if (str[i] < str[minIndex])
                        minIndex = i;
            
                // Append the smallest character to result
                res.push_back(str[minIndex]);
            
                // substring starting from minIndex+1 to str.length() - 1.
                string new_str = str.substr(minIndex+1, len-minIndex);
            
                // Recur for the above substring and n equals to n-minIndex
                buildLowestNumberRec(new_str, n-minIndex, res);
            }
            
            // A wrapper over buildLowestNumberRec()
            string buildLowestNumber(string str, int n)
            {
                string res = "";
            
                // Note that result is passed by reference
                buildLowestNumberRec(str, n, res);
            
                return res;
            }
            
            // Driver program to test above function
            int main()
            {
                string str = "121198";
                int n = 2;
                cout << buildLowestNumber(str, n);
                return 0;
            }
            

            【讨论】:

              【解决方案10】:
              #include <cmath>
              #include <cstdio>
              #include <vector>
              #include <queue>
              #include <functional>
              #include <iostream>
              #include <utility>
              #include <algorithm>
              #define mod 1000000007
              #define ll long long
              using namespace std;
              bool Compare (pair<int, int> p1, pair<int, int> p2)  {
                  if (p1.first < p2.first) {
                      return true;
                  }
                  return false;
              }
              int main() {
                  priority_queue <pair<int, int>, vector <pair <int, int> >, function<bool (pair <int, int>, pair <int, int>) > > pq (Compare);
                  int n, k;
                  cin>>n>>k;
                  int i = 1;
                  while (n) {
                      pq.push(make_pair(n%10, i));
                      n = n/10;
                      i++;
                  }
                  for(int j =1; j<=k;j++){
                      pq.pop();
                  }
                  int number = pq.top().first;
                  int index = pq.top().second;
                  int digit = 1;
                  pq.pop();
                  while(!pq.empty()){
                      int current_index = pq.top().second;
                      digit = digit * 10;
                      if(current_index < index) {
                          number = number * 10 + pq.top().first;
                      } else {
                          number = digit * pq.top().first + number;
                      }
                      pq.pop();
                  }
                  cout<<number<<endl;
                  return 0;
              }
              

              我使用priority_queue 和pair 解决了这个问题。请告诉我是否有更好的解决方案

              【讨论】:

                【解决方案11】:

                这个想法是从头开始遍历字符串并删除大于它后面的数字的第一个数字。 如果不存在这样的数字,则删除最后一个数字。

                    class Solution {
                    private:
                        void removeOneDigit(string& s){
                            for(int i=0; i<s.length()-1; i++){
                                if(s[i+1]<s[i]){
                                    s.erase(i,1);
                                    return;
                                }
                            }
                            s.erase(s.length()-1,1);
                        }
                    public:
                        string removeKdigits(string num, int k) {
                            for(int i=0; i<k; i++){
                                removeOneDigit(num);
                            }
                            int i=0;
                            while(num[i]=='0'){
                                num.erase(0,1);
                            }
                            if(num.length()==0){return "0";}
                            return num;
                        }
                    };
                

                【讨论】:

                  猜你喜欢
                  • 2015-06-16
                  • 1970-01-01
                  • 2019-04-01
                  • 2020-01-05
                  • 2013-11-28
                  • 1970-01-01
                  • 2017-06-01
                  • 1970-01-01
                  • 2016-01-16
                  相关资源
                  最近更新 更多