繁体   English   中英

从输入数字中删除k位后如何获得最少的数字

[英]How to get the least number after deleting k digits from the input number

例如,如果输入的数字是24635 ,则删除任意 3 位数字后的最小数字为23

这与取最小的两个数字不同,因为必须保持数字的顺序。

删除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)

提供递归方法。

在每次迭代测试成功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

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

非常简单的解决方案。

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

最后的nk-1位数字显然不能是答案的第一个数字,后面没有足够的数字来制作足够长的数字。

因此,我们简单地忽略最后的nk数字,并专注于前k数字并找到该k数字集合中的最小数字。 那将是我们答案的第一个数字。 例如,所有的三位数字 5XX 都小于形式为 6XX 的所有数字,无论 X 是什么。 (如果有平局,其中两位数字共享最小的荣誉,请选择左侧的数字,因为它稍后会给您更多的灵活性。)

现在你知道第一个数字是什么,你可以忽略它和左边的所有东西,用剩下的数字递归地重复整个事情——我能从剩下的数字中找出最小的数字是多少?

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

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

  • n := num 的大小

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

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

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

  • 将 num[i] 插入 st

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

  • 当堆栈不为空时

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

  • 现在反转 ret 字符串

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

  • 而 i < ret 的大小和 ret[i] 不是 '0'

  • 将 i 增加 1

  • 对于 i < ret 的大小

  • ans := ans + ret[i]

  • 回复 := 答案

  • 如果 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;
   }
};

我想到了非常简单的算法

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

例如 :-

给定的数字是24635 ,要删除的数字没有是2

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

知道您想保持数字顺序肯定会使此解决方案更加困难。

我同意IVlad 的观点,即char[]不是要走的路。

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

#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;
}

我认为这可以解决问题:

让我们使用 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. 我们没有更多的位数要删除。

实现此方法取决于您。

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);
 }

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

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

buildLowestNumber(str, n, res)

1) 如果 n == 0,则没有要删除的内容。 将整个 'str' 附加到 'res' 并返回

2) 让'len' 是'str' 的长度。 如果 'len' 小于或等于 n,则可以删除所有内容。 不向 'res' 附加任何内容并返回

3) 在'str'的前(n+1)个字符中找出最小的字符。 设最小字符的索引为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;
}
#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 解决了这个问题。 请让我知道是否有更好的解决方案

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

    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;
        }
    };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM