题目描述
给定一个非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。 注意: num 的值小于 10^10000 且 ≥10^ k。 num 不会包含任何前导零。 示例 1 : 输入: num = 1432219, k = 3 输出: 1219 解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。 示例 2 : 输入: num = 10200, k = 1 输出: 200 解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。 示例 3 : 输入: num = 10, k = 2 输出: 0 解释: 从原数字移除所有的数字,剩余为空就是0。
题目分析
这道题可以从删除多余元素和直接获得结果元素出发。先说删除多元素的算法,我们需要将当前递减序列如4321,依次删除4,3...,是不是一直按照非严格递增的顺序前进,删除递减序列就行了呢?遇到有相同数字怎么办呢?比如说:44321,当前删除最后一个4后得到4321,指针停留在3的位置,如果我们直接看后一个元素与3的大小关系,就有问题了(前面还有一个4),这种情况可以采用观察当前元素和前一个元素的方法,删除了元素需要将下标减2。除此之外,还需要将数组进行移动,还要处理。这种方法是不是很麻烦且容易出错,用直接获得结果元素就简单多了。假设num=1463,k=1,我们构造一个区间即start=0、end=k=1,然后在这个区间中找最小值加入结果字符串中,即在14中选最小,得到1,更新start=0;然后我们继续++start(得到1),end++(得到2),然后在46中选最小值,得到4,加入结果集字符串中,更新start=1,然后同上面步骤直到end=4。这样就可以保证我们能删除k个元素,且保证当前最高位的数值最小,为什么初始化时要选k+1的区间大小呢?因为前k+1个数内一定存在结果字符串中起始元素,因为只删除k个元素(防止num=400000,k=1的情况中4被加入结果集中,后面的区间大小可能小于k+1)。从当前的区间中选一个最小数值意味着当前最高位是当前最小的。为什么结果集中的第二个元素必须在当前end之前呢,因为我们知道首元素是初始化区间中的最小值,这样才能保证最高位是最小的,首元素确定后,他前面的元素肯定不能选,只能选其后,如果超过了end那么最后删除的元素将多余k个,我们只能在首元素之后和end之前选最小。看出来了吗?这个算法真的很巧妙。
算法实现
删除多余元素
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
int num;
cin >> num;
char** result = new char*[num];
for (int i = 0;i < num;i++)
{
char* number=new char[10000];
int k;
cin >> number;
cin >> k;
int size = strlen(number);
for (int j = 1;j < size;j++)
{
if (number[j-1] > number[j] && --k >= 0)
{
char* src = number+j;
char* dst = number + j - 1;
while ((*dst++ = *src++) != '\0');
j=0;
size--;
}
}
while (k > 0)
{
number[--size] = '\0';
k--;
}
if (size <= 0)
{
result[i] = new char[2];
result[i][0] = '0';
result[i][1] = '\0';
}
else
{
size--;
int m = 0;
for (;m < size;m++)
if (number[m] != '0')
break;
result[i] = new char[size - m + 2];
char* src = number+m;
char* dst = result[i];
while ((*dst++ = *src++) != '\0');
}
/*cout << result[i];
cout << endl;*/
}
for (int i = 0;i < num;i++)
{
for (int j = 0;j < strlen(result[i]);j++)
{
cout << result[i][j];
}
cout << endl;
}
return 0;
}
直接增加元素
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
int num;
cin >> num;
char** result = new char*[num];
for (int i = 0;i < num;i++)
{
char* number=new char[10001];
int k;
cin >> number;
cin >> k;
int size = strlen(number);
if(k==size)
{
result[i]=new char[2];
result[i][0]='0';
result[i][1]='\0';
continue;
}
int curSize=size-k;
char* cur=new char[curSize+1];
int count=0;
int start=0,end=k;
while(end<size)
{
char min=number[start];
int minIndex=start;
for(int j=start+1;j<=end;j++)
{
if(number[j]<min)
{
min=number[j];
minIndex=j;
}
}
cur[count++]=min;
start=minIndex+1;
end++;
}
cur[curSize]='\0';
int m = 0;
for (;m < curSize;m++)
if (cur[m] != '0')
break;
if(m==curSize)
{
result[i]=new char[2];
result[i][0]='0';
result[i][1]='\0';
continue;
}
result[i] = new char[curSize - m];
char* src = cur+m;
char* dst = result[i];
while ((*dst++ = *src++) != '\0');
}
for (int i = 0;i < num;i++)
{
cout<<result[i];
cout << endl;
}
return 0;
}