n个数字组成的字符串,移走k个字符,求剩下的字符组成的最小数字

试题一

给出n个数字组成的字符串,移走k个字符,求剩下的字符组成的最小数字;

示例
  • 字符串:132,移走1个字符, 输出:12
  • 字符串:001032,移走2个字符,输出: 2
  • 字符串: 132450, 移走2个字符,输出:1240
解答

移走k个字符,可以转换为移走1个字符,移k次,每一次都让剩下的字符组成的数字最小;

这样转换意味着,每次操作的最优解,就是多次操作同时进行后的最优解(局部最优累积成为全局最优),需要对此进行证明,之后我们会使用严格的数学推导方式得出操作的规律,从而证明局部最优累积为全局最优;

这可以理解为贪心算法的思想,但本题目是有严格解的,并不是绝对的贪心算法;

先看移走1个字符的情形:

设n个字符,排列顺序为 a 1 a 2 . . . a n a_{1}a_{2}...a_{n} a1a2...an a 1 ≠ 0 a_{1}\neq 0 a1=0, 那么该字符串表示的数字为
S n = a 1 × 1 0 n − 1 + a 2 × 1 0 n − 2 + . . . + a i − 1 × 1 0 n − i + 1 + a i × 1 0 n − i + a i + 1 × 1 0 n − i − 1 + . . . + a n − 1 × 10 + a n S_{n}=a_{1}\times 10^{n-1}+a_{2}\times 10^{n-2}+...+a_{i-1}\times 10^{n-i+1}+a_{i}\times 10^{n-i}+a_{i+1}\times 10^{n-i-1}+...+a_{n-1}\times 10+a_{n} Sn=a1×10n1+a2×10n2+...+ai1×10ni+1+ai×10ni+ai+1×10ni1+...+an1×10+an

如果移走的字符是 a i a_{i} ai 1 ≦ i ≦ n 1\leqq i\leqq n 1in,则剩下的字符组成的数字为

f ( i ) = a 1 × 1 0 n − 2 + a 2 × 1 0 n − 3 + . . . + a i − 1 × 1 0 n − i + a i + 1 × 1 0 n − i − 1 + . . . + a n − 1 × 10 + a n {}f\left( i\right) =a_{1}\times 10^{n-2}+a_{2}\times 10^{n-3}+...+a_{i-1}\times 10^{n-i}+a_{i+1}\times 10^{n-i-1}+...+a_{n-1}\times 10+a_{n} f(i)=a1×10n2+a2×10n3+...+ai1×10ni+ai+1×10ni1+...+an1×10+an

从f(i)的表达式不容易看出问题,但Sn和f(i)之间是存在关系的,且Sn是固定值,因此计算差值

Δ S i =   S n − f ( i ) = a 1 × 1 0 n − 1 + ( a 2 − a 1 ) × 1 0 n − 2 + . . . + ( a i − a i − 1 ) × 1 0 n − i \Delta S_{i}=\ S_{n}-f\left( i\right) =a_{1}\times 10^{n-1}+(a_{2}-a_{1})\times 10^{n-2}+...+(a_{i}-a_{i-1})\times 10^{n-i} ΔSi= Snf(i)=a1×10n1+(a2a1)×10n2+...+(aiai1)×10ni
如果要求f(i)最小, Δ S i \Delta S_{i} ΔSi则要求最大;

所以,我们需要观察 Δ S i \Delta S_{i} ΔSi的表达式,找到跟i的规律;

Δ S 1 = a 1 × 1 0 n − 1 ; \Delta S_{1}=a_{1}\times 10^{n-1}; ΔS1=a1×10n1;

Δ S 2 = a 1 × 1 0 n − 1 + ( a 2 − a 1 ) × 1 0 n − 2 \Delta S_{2}=a_{1}\times 10^{n-1}+\left( a_{2}-a_{1}\right) \times 10^{n-2} ΔS2=a1×10n1+(a2a1)×10n2

Δ S 3 = a 1 × 1 0 n − 1 + ( a 2 − a 1 ) × 1 0 n − 2 + ( a 3 − a 2 ) × 1 0 n − 3 \Delta S_{3}=a_{1}\times 10^{n-1}+\left( a_{2}-a_{1}\right) \times 10^{n-2}+\left( a_{3}-a_{2}\right) \times 10^{n-3} ΔS3=a1×10n1+(a2a1)×10n2+(a3a2)×10n3

  • 从表达式来看,如果我们从高位到低位来移动字符(即从第一位到最后一位的顺序),每移动后一位字符时,就会多出一个 ( a i − a i − 1 ) × 1 0 n − i (a_{i}-a_{i-1})\times 10^{n-i} (aiai1)×10ni项,而对于未知的数字,并不能判断 ( a i − a i − 1 ) (a_{i}-a_{i-1}) (aiai1)是否>0,所以应该分情况讨论;

    • 如果 ( a i − a i − 1 ) > 0 (a_{i}-a_{i-1})>0 (aiai1)>0,那么就继续向第i+1个字符的方向观察,因为此时多出来的那一项是正数,只会让 Δ S i \Delta S_{i} ΔSi越来越大;
    • 如果 ( a i − a i − 1 ) < 0 (a_{i}-a_{i-1})<0 (aiai1)<0,那么就要移走第i-1个字符了,防止出现负数;
  • 另外,从表达式来看,高位的权重大(a1的权重是 1 0 n − 1 10^{n-1} 10n1),应该从高位来开始判断;

    • 如果a2 < a1,那么不能让(a2-a1)出现,则要移走a1即可;
    • 如果a2 > a1, 那么继续看a3和a2的关系,重复本过程;

所以,综上所述,应该从最高位开始判断,一旦出现 a i < a i − 1 a_{i}<a_{i-1} ai<ai1, 那么就移动 a i − 1 a_{i-1} ai1,否则继续判断下一位;

再看移走k个字符

移走k个字符的情况,表达式中无非就是缺少了某些项,但跟移走1个字符的思路是一致的,规律是一致的,因此移走k个字符与移动k次,每次移走1个字符是等效的;

程序设计
/**
 * 
 * @param {*} inputStr: string,满足/[0-9]{n}/正则规则, 例如132450
 * @param {*} k: integer, k 应该小于inputStr去除开头为0的字符后的长度,例如inputStr为00132450, k < 6而不是8
 */
function minimize(inputStr, k) {
  // 先去除字符串开头的0
  let handledArr = inputStr.split('');
  while(handledArr[0] === '0') {
    handledArr = handledArr.slice(1);
  }
  // 开始取走字符
  for(let i = 0; i < k; i ++) {
    let j = 0;
    while (handledArr[j] < handledArr[j + 1]) {
      j ++;
    }
    handledArr.splice(j, 1);
  }
  while(handledArr[0] === '0') {
    handledArr = handledArr.slice(1);
  }
  return handledArr.join('');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值