对一道编程题的后续思考

  原题来自《一道整数求值作业》

  题目描述:给定一个整数 X,找到组成的数字和 X 完全相同的,且大于 X 的最小的那个数;若不存在这样的数,输出 0

  一开始把它想复杂了,后来想想只需将该数组成的数列从后往前枚举,然后判断当前位置往后的数列是否是降序即可,时间复杂度O(n),详细思路跟上文一致可以参考原文。

var a = 21544221; // 要求的数字
var s = a.toString();
var len = s.length;
for(var i = len - 2; i >= 0; i--) {
  if(s[i] >= s[i + 1]) continue;
  for(var j = len - 1; j > i; j--) {
    if(s[j] > s[i]) {
      var index = j;
      break;
    }
  }
  break;
}
var arr = s.split(‘‘);
if(i !== -1) {
  arr[i] = [arr[index], arr[index] = arr[i]][0];
  var ans = arr.slice(0, i + 1).concat(arr.slice(i + 1).reverse()).join(‘‘);
}
console.log(~~ans);

  之后我陷入了思考:如果不是要求比原数大的最小的数,而是第k大的呢

  为了不至于复杂度太高以致cpu假死,我把所求数暂定在int32范围内,写了个计算第k大数的暴力程序用来验证,复杂度O(n!)

function dfs(a, index) {
  if(index === len) {
    if(hash_ans[a] || a < num)
      return;
    ans.push(a);
    hash_ans[a] = true;
    return;
  }

  for(var i = 0; i < len; i++) {
    if(hash_pos[i]) continue;
    hash_pos[i] = true;
    dfs(~~a * 10 + ~~arr[i], index + 1);
    hash_pos[i] = false;
  }
}

var num = 131443134; // 要求的数字
var k = 818; // 比num大的第k大数
var arr = num.toString().split(‘‘);
var len = num.toString().length;
var ans = []; // 存放比num大的数
var hash_pos = {};
var hash_ans = {};
dfs(0, 0);
ans.sort();
ans.length <= k ? console.log(0) : console.log(ans[k]);

  然后开始思索第k大解,发现后向扫描仍然可解,但是复杂度应该不低,在没有想到其他解法前先一试。后向扫描的依据是所组成的新的数从前往后跟原来的数比较,在经过一系列相等的比较后,肯定会出现一个大于原数的相同位置。于是我们根据此枚举该位置,枚举到的位置的数用位置后面的比它大的数代替(以保证新的数比原数大),确定该位置的数之后,后面的位置进行全排列即是确定该位置后的所有解(注意全排列后需除相同数字的全排列),同时我们需要维护一个变量保存当前枚举位置后的总的解个数,如果个数还没达到k,则继续在该位置枚举更大的数,或者枚举位置继续前移;当发现总个数大于k时,该位置就确定是这个数了,接着进行第二轮枚举,枚举过程相似。在枚举的过程中反复地出现某个临界点(大于小于k),而出现临界点的同时该位置的数确定。

var A = [];
A[0] = 1;
for(var i = 1; i <= 10; i++)
  A[i] = A[i - 1] * i;
var a = 131443134;
var k = 818;

var total = 0;
var arr = a.toString().split(‘‘); // 字符串数组
var len = arr.length;
var hash = [];  // 记录后向扫描中0~9的数量
for(var i = 0; i <= 9; i++)
  hash[i] = 0;
hash[a % 10] = 1;
var maxn = a % 10;  // 维护后向扫描中最大的数字

for(var i = len - 2; i >= 0; i--) {
  hash[~~arr[i]]++;
  if(maxn > ~~arr[i]) { // 可以交换
    for(var j = ~~arr[i] + 1; j <= 9; j++) {  // 该位置可以换的数字
      if(!hash[j]) continue;
      var add = A[len - i - 1];  // 全排列
      for(var l = 0; l <= 9; l++) {
        l === j ? add /= A[hash[l] - 1] : add /= A[hash[l]]; // 除去相同数字的全排列
      }

      if(total + add >= k) {  // 就是这个交换
        hash[j]--;
        // 第i位变成了j
        var ans = a.toString().substring(0, i) + j + getAns(i);
      } else {
        total += add;
      }

      if(ans) break;
    }
  }
  maxn = Math.max(maxn, ~~arr[i]);
  if(ans) break;
}

function getAns(a) { // 第a位变成了b
  var res = ‘‘;
  for(var i = a + 1; i < len; i++) {
    for(var j = 0; j <= 9; j++) { // 从小到大枚举i位置的数字
      if(!hash[j]) continue;
      var add = A[len - i - 1];  // 全排列
      for(var l = 0; l <= 9; l++)
        l === j ? add /= A[hash[l] - 1] : add /= A[hash[l]]; // 除去相同数字的全排列
      if(total + add >= k) {  // 就是这个交换
        hash[j]--;
        res += j;
        break;  // i位置确定是j
      } else {
        total += add;
      }
    }
  }
  return res;
}

ans ? console.log(ans) : console.log(0);

  解是有了,好像没涉及什么算法,倒是觉得像个复杂的模拟,看了下时间复杂度,最坏情况下应该会达到O(n*10*10),似乎效率还可以,但是我一开始想到这个问题的时候,似乎觉得会是一道动态规划,或者是按位dp?无奈对于算法了解不多,只能日后接触了再进行思考,也希望有更高效解法的博友留言交流

时间: 2024-08-28 11:41:06

对一道编程题的后续思考的相关文章

一道CTF题引发的思考-MySQL的几个特性

0x01   背景 前天在做一道CTF题目时一道盲注题,其实盲注也有可能可以回显数据的,如使用DNS或者HTTP等日志来快速的获取数据,MYSQL可以利用LOAD_FILE()函数读取数据,并向远程DNS主机发送数据信息,此时DNS日志文件中就会有盲注语句的查询结果.这里不做这部分的讨论,只是说下有这种方法,在这道题目中我是使用常规的盲注的方式获取数据的.其中遇到有以下几个问题: 过滤规则的判断与绕过 MySQL的一些少有人总结的特性 手动盲注的繁琐低效 这题确实让我思考了很多,当然还有一些特性

一道编程题实在是不知道哪个地方错了

求帮忙 一道觉得很简单的编程题,提交了很多次,但是,还是wrong,实在是崩溃啊,真的不知道哪里错了,希望大家可以帮忙解决一下,感激不尽. 就是这道题----Conversions Description Conversion between the metric and English measurement systems is relatively simple. Often, it involves either multiplying or dividing by a constant

Airbnb coding面的一道编程题

之前在直播的时候Airbnb负责人说他们的coding题难度不会到dp,我就知道肯定是一些字符串处理啥的编程题了. 果然,Airbnb的coding面是在codePad上手写代码,面试官希望能看到你书写的过程,所以少用本地ide(呵呵,不调试让写编程题是最XX的,所以有些东西你需要及时沟通) 自己给的三四个test case,要求输出对应的结果就行了(比ACM是相当宽松了). 题目是这样的,很简单: 有这样的文本(我转js的字符串了) let str1 = 'rwer321,dad,adas,y

一道CTF题引发的思考-MySQL的几个特性(续)

0x00 背景 这两天处于转牛角尖的状态,非常不好.但是上一篇的中提到的问题总算是总结了些东西. 传送门:疑问点0x02(4) 0x01 测试过程 (1)测试环境情况:创建了如下测试表test, mysql> select * from test;+---------+-------+-----------------------------------------+| user_id   | user  | password    |+---------+-------+-----------

thoughtworksd一道编程题

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 商品税的计算: 国内商品除食物.书籍

赛码网的一道百度编程题

最近偶尔接触到这个赛码网,看了百度的一道编程题,于是尝试了一下,发现虽然天天写代码实现这个居然花了我好长时间,仍然没有通过全部案例.目前给的通过率是83% 题目如下: 小B最近对电子表格产生了浓厚的兴趣,她觉得电子表格很神奇,功能远比她想象的强大.她正在研究的是单元格的坐标编号,她发现表格单元一般是按列编号的,第1列编号为A,第2列为B,以此类推,第26列为Z.之后是两位字符编号的,第27列编号为AA,第28列为AB,第52列编号为AZ.之后则是三位.四位.五位--字母编号的,规则类似. 表格单

上海华为的一道关于指针方面的编程题(C/C++)

int A[nSize],其中隐藏着若干0,其余非0整数,写一个函数int Func(int* A, int nSize),使A把0移至后面,非0整数移至数组前面并保持有序,返回值为原数据中第一个元素为0的下标. 尽可能不使用辅助空间且考虑效率及异常问题,注释规范且给出设计思路 注:我的方法的复杂度为O(n),大家如果有其它方法希望可以交流一下. /* author: jiangxin Blog: http://blog.csdn.net/jiangxinnju */ #include <ios

一道模板元编程题源码解答(replace_type)

今天有一同学在群上聊到一个比较好玩的题目(本人看书不多,后面才知是<C++模板元编程>第二章里面的一道习题), 我也抱着试一试的态度去完成它, 这道题也体现了c++模板元编程的基础和精髓: 类型就是数据. 题目如下所述: Write a ternary metafunction replace_type<c,x,y> that takes an arbitrary compound type c as its first parameter, and replaces all oc

【求助】一道考验脑细胞的编程题

要求计算S的面积.注意:仅计算面积,不区分正负,如果围成的图形被x轴分割为上下两部分,那么就求上下两部分面积之和. 输入多项式fx,以字符串表示,格式为:4*x^5-x^2+5*x+12,多项式表达式不包含括号,可能包含空格.数字.字母x.^.*.+.-,保证多项式最高次幂为非负整数,且最高次幂不超过10. 表达式中4*x^5与4x^5等价,如下面的表达式是合法的: x^10-5x^1 -4*x^1 + 5x^0 2.3x^4 - 2.56*x + 1 输入不会出现下列类型的表达式: x(x+5