删除k位数字使得最终的数字最小算法

题目描述

给定一个非负整数 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;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值