华为OD算法题汇总

560 阅读14分钟

1.1【用连续自然数之和来表达整数】

【用连续自然数之和来表达整数】一个整数可以由连续的自然数之和来表示。给定一个整数,计算该整数有几种连续自然数之和的表达式,且打印出每种表达式。 /**

  • 输入描述: 一个目标整数T (1 <=T<= 1000) 输出描述:
  • 该整数的所有表达式和表达式的个数。如果有多种表达式,
  • 输出要求为:
    1. 自然数个数最少的表达式优先输出
  • 2.每个表达式中按自然数递增的顺序输出,具体的格式参见样例。
  • 在每个测试数据结束时,输出一行”Result:X”,其中X是最终的表达式个数
  • 示例1:
  • 输入
  • 9
  • 输出
  • 9=9
  • 9=4+5
  • 9=2+3+4
  • Result:3 */
const handleSum = (val) => {
 console.log(`${val}=${val}`);
 const arr = [];
 for(let i = 1; i < val; i++) {
   let sum = 0;
   let str = '';
   for(let j = i; sum < val; j++) {
     sum += j;
     str += `${j}+`;
     if (sum === val) {
       arr.push(`${val}=${str.slice(0, str.length - 1)}`);
       break;
     }
   }
 }
 arr.sort((a, b) => a.length - b.length);
 arr.forEach(item => console.log(item));
 console.log(`Result:${arr.length + 1}`);
}
// 测试用例
handleSum(9);

1.2 【判断一组不等式是否满足约束并输出最大差】

/**

  • 【判断一组不等式是否满足约束并输出最大差】分值100
  • 【判断一组不等式是否满足约束并输出最大差】给定一组不等式,判断是否成立并输出不等式的最大差(输出浮点数的整数部分),
  • 要求:
  • 1)不等式系数为double类 型, 是一个二维数组;
  • 2)不等式的变量为int类型,是一维数组;
  • 3)不等式的目标值为double类型,是一维数组;
  • 4)不等式约束为字符串数组,只能 是:">",">=","<","<=","=",
  • 例如,不等式组:
  • a11x1+a12x2+a13x3+a14x4+a15*x5<=b1;
  • a21x1+a22x2+a23x3+a24x4+a25*x5<=b2;
  • a31x1+a32x2+a33x3+a34x4+a35*x5<=b3;
  • 最大差=max{ (a11x1+a12x2+a13x3+a14x4+a15x5-b1), (a21x1+a22x2+a23x3+a24x4+a25x5-b2), (a31x1+a32x2+a33x3+a34x4+a35*x5-b3) },类型为整数(输出浮点数的整数部分) */
const handleMaxRes = (val) => {
  const str = val.split(";")
  // 等式的数量
  const num_eq = str[str.length - 1 ].split(",").length
  // 系数和未知数的数量
  const num_x = str[0].split(",").length
  const arr = []
  const x = []
  const b = []
  // 处理符号
  const eq = str[num_eq + 2].split(',')
  const res = []
  let flag = true
  // 处理arr
  for (let i = 0; i < num_eq; i++) {
    const temp1 = str[i].split(',')
    arr.push(temp1)
  }
  // 处理x
  const temp2 = str[num_eq].split(',')
  temp2.forEach((item, index) => {
    x[index] = parseInt(item)
  });
  // 处理b
  const temp3 = str[num_eq + 1].split(',')
  temp3.forEach((item, index) => {
    b[index] = parseFloat(item)
  })
  for (let i = 0; i < num_eq; i++) {
    let temp = 0.0
    for (let j = 0; j < num_x; j++) {
      temp += parseFloat(arr[i][j]) * parseInt(x[j])
      
    }
    if (eq[i] === '<=') {
      flag = temp <= parseFloat(b[i]) ? flag && true : flag && false
    } else if (eq[i] === '<') {
      flag = temp < parseFloat(b[i]) ? flag && true : flag && false
    } else if (eq[i] === '>=') {
      flag = temp >= parseFloat(b[i]) ? flag && true : flag && false
    } else if (eq[i] === '>') {
      flag = temp > parseFloat(b[i]) ? flag && true : flag && false
    } else if (eq[i] === '=') {
      flag = temp === parseFloat(b[i]) ? flag && true : flag && false
    }
    res[i] = parseInt(temp - parseFloat(b[i]))
  }
  const max = Math.max(...res)
  console.log(`${flag} ${max}`)
}
// 测试用例
// 输出 false 458
const res = "2.3,3,5.6,7,6;11,3,8.6,25,1;0.3,9,5.3,66,7.8;1,3,2,7,5;340,670,80.6;<=,<=,<="
handleMaxRes(res)

2.1 【找朋友】

/**

  • 【找朋友】分值100
  • 【找朋友】 在学校中,N个小朋友站成一队, 第i个小朋友的身高为height[i],
  • 第i个小朋友可以看到的第一个比自己身高更高的小朋友j,那么j是i的好朋友(要求j > i)。
  • 请重新生成一个列表,对应位置的输出是每个小朋友的好朋友位置,
  • 如果没有看到好朋友,请在该位置用0代替。 小 朋友人数范围是 [0, 40000]。
  • 输入描述:
  • 第一行输入N,N表示有N个小朋友
  • 第二行输入N个小朋友的身高height[i],都是整数
  • 输出描述:
  • 输出N个小朋友的好朋友的位置
  • 示例1:
  • 输入
  • 2
  • 100 95
  • 输出
  • 0 0 */
const computArea = (num, arr) => {
  const list = []
  for (let i = 0; i <num; i++) {
    for (let j = i + 1; j < num; j++) {
      if (arr[i] < arr[j]) {
        list.push(j)
        break
      }
      if (j === arr.length -1) {
        list.push(0)
      }
      
    }
    if (i === arr.length -1) {
      list.push(0)
    }
    
  }
  console.log(list.join(" "))
}
// 测试用例
const num = 8
const arr = [123, 124, 125, 121, 119, 122, 126, 123]

computArea(num, arr)

2.2 【靠谱的车】

/**

  • 【靠谱的车】 程序员小明打了一辆出租车去上班。出于职业敏感,他注意到这辆出租车的计费表有点问题,总是偏大。
  • 出租车司机解释说他不喜欢数字4,所以改装了计费表,任何数字位置遇到数字4就直接跳过,其余功能都正常。
  • 比如: 1. 23再多一块钱就变为25; 2. 39再多一块钱变为50; 3. 399再多一块钱变为500; 小明识破了司机的伎俩,准备利用自己的学识打败司机的阴谋。
  • 给 出计费表的表面读数,返回实际产生的费用。
  • 输入描述: 只有一行,数字N,表示里程表的读数。 (1<=N<=888888888)。
  • 输出描述:一个数字,表示实际产生的费用。以回车结束。
  • 示例1:
  • 输入
  • 5
  • 输出
  • 4 */
const getOriginalVal = (val) => {
  const num = parseInt(val)
  let count = 0
  for (let i = 0; i <= num; i++) {
    if (String(i).includes('4')) {
      count += 1
    }
    
  }
  console.log(num - count)
}

getOriginalVal(5)

2.3 【数组二叉树】

/**

  • 【数组二叉树】分值200
  • 二叉树也可以用数组来存储,给定一个数组,树的根节点的值存储在下标1,对于存储在下标N的节点,它的左子节点和右子节点分别存储在下标2N和2N+1,并且我们用值-1代表一个节点为空
  • 给定一个数组存储的二叉树,试求从根节点到最小的叶子节点的路径,路径由节点的值组成。
  • 输入描述:
  • 输入一行为数组的内容,数组的每个元素都是正整数或表示节点为空的-1,元素间用空格分隔。注意第一个元素即为根节点的值,即数组的第N个元素对应下标N ,下标0在树的表示中没有使用,所以我们省略了。输入的树最多为7层。
  • 输出描述:
  • 输出从根节点到最小叶子节点的路径上,各个节点的值,由空格分隔,用例保证最小叶子节点只有一个。
  • 示例1:
  • 输入
  • 3 5 7 -1 -1 2 4
  • 输出
  • 3 7 2 */

/**

  • 叶子节点的判断
  • return (2 * index +1 >= arr.length || arr[2 * index + 1] === -1) && (2 * index +2 >= arr.length || arr[2 * index + 2] === -1) */

/**

  • 叶子节点所有父节点的查找
  • while (index > 0) { res.push(arr[index]) index = (index -1)/2 } */
 /**
  * 判断是否是叶子节点
  */
 const isLeaf = (arr, index) => {
  return (2 * index +1 >= arr.length || arr[2 * index + 1] === -1) &&  (2 * index +2 >= arr.length || arr[2 * index + 2] === -1)
 }

 /**
  * 返回最小叶子节点对应索引
  */

 const dfs = (res, index) => {
   if (isLeaf(res, index)) {
     return index
   } else {
     const index_left = dfs(res, 2 * index + 1)
     const index_right = dfs(res, 2 * index + 2)
     if (index_left >= res.length || res[index_left] === -1) {
       return index_right
     } else if (index_right >= res.length || res[index_right] === -1) {
       return index_left
     } else {
       return res[index_left] < res[index_right] ? index_left: index_right
     }
   }
 }

 const handleArrayBiarayTree = (val) => {
   const res = (val.split(" ") || []).map(item => parseInt(item))
   // 找到叶子节点的索引
   let index = dfs(res, 0)
   const arr = []
   // 找到最小叶子节点的所有父节点的索引
   while (index > 0) {
     res[index] && arr.push(res[index])
     index = (index - 1 )/2
   }
   arr.push(res[0])
   arr.reverse()
   console.log(arr.join(" "))
 }
 // 测试用例

 const val = "3 5 7 -1 -1 2 4"
 const val2 = "5 9 8 -1 -1 7 -1 -1 -1 -1 -1 6"

 handleArrayBiarayTree(val)
 handleArrayBiarayTree(val2)

3.1 【两数之和绝对值最小】

/**

  • 【两数之和绝对值最小】分值100
  • 【两数之和绝对值最小】给定一个从小到大的有序整数序列(存在正整数和负整数)数组 nums ,
  • 请你在该数组中找出两个数,其和的绝对值(|nums[x]+nums[y]|) 为 最小值,并返回这个绝对值。
  • 每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
  • 输入描述:
  • 一个通过空格分割的有序整数序列字符串,最多1000个整数,且整数数值范围是 -65535~65535。
  • 输出描述:
  • 两数之和绝对值最小值
  • 示例1:
  • 输入
  • -3 -1 5 7 11 15
  • 输出
  • 2 */
const sum = (val) => {
  const arr = val.split(" ")
  let res = []
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      const num = Math.abs(parseInt(arr[i]) + parseInt(arr[j]))
      res.push(num)
    }
    
  }
  res = res.sort((a, b) => a - b)
  console.log(res[0])
}

// 测试用例
sum("-3 -1 5 7 11 15 16")

3.2 【最大花费金额】

/**

  • 【最大花费金额】分值100
  • 【最大花费金额】双十一众多商品进行打折销售,小明想购买自己心仪的一些物品,但由于受购买资金限制,
  • 所以他决定从众多心仪商品中购买三件,而且想尽可能 的花完资金,现在请你设计一个程序帮助小明计算尽可能花费的最大资金数额。
  • 输入描述:
  • 输入第一行为一维整型数组M,数组长度小于100,数组元素记录单个商品的价格,单个商品价格小于1000。 输 入第二行为购买资金的额度R,R小于100000。
  • 输出描述:
  • 输出为满足上述条件的最大花费额度。 注意:如果不存在满足上述条件的商品,请返回-1。
  • 示例1:
  • 输入
  • 23,26,36,27
  • 78
  • 输出
  • 76 */
const sum = (str, val) => {
  const arr = str.split(",")
  let sum = 0
  let max = -1
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      for (let k = j + 1; k < arr.length; k++) {
        sum = parseInt(arr[i]) + parseInt(arr[j]) + parseInt(arr[k])
        if (sum <= val && sum > max) {
          max = sum
        }
      }
      
    }
    
  }
  console.log(max)
}

// 测试用例
const str = "23,26,36,27"
sum(str,78)

3.3 【计算疫情扩散时间】

/**

  • 【计算疫情扩散时间】分值200
  • 【计算疫情扩散时间】在一个地图中(地图由n*n个区域组成),有部分区域被感染病菌。
  • 感染区域每天都会把周围(上下左右)的4个区域感染。
  • 请根据给定的地图计算,多少天以后,全部区域都会被感染。 如果初始地图上所有区域全部都被感染,或者没有被感染区域,返回-1
  • 输入描述:
  • 一行N*N个数字(只包含0,1,不会有其他数字)表示一个地图,数字间用,分割,0表示未感染区域,1表示已经感染区域 每 N个数字表示地图中一行,输入数据共表示N行N列的区域地图。
  • 例如输入1,0,1,0,0,0,1,0,1,表示地图
  • 1,0,1
  • 0,0,0
  • 1,0,1
  • 输出描述:
  • 一个整数,表示经过多少天以后,全部区域都被感染
  • 示例1:
  • 输入
  • 1,0,1,0,0,0,1,0,1
    
  • 输出
  • 2 */
/**
* 
* @param {Array} rank  [ [ '1', '0', '1' ], [ '0', '0', '0' ], [ '1', '0', '1' ] ]
* @param {Array} copyRank  rank的复制品
* @param {Number} days  需要感染的天数
* @returns 
*/

const spread = (rank, copyRank, days) => {
 for (let i = 0; i < rank.length; i++) {
   for (let j = 0; j < rank.length; j++) {
     if (rank[i][j] === "1") {
       continue
     }
     // 下边扩散
     if (i > 0) {
       if (copyRank[i - 1][j] === "1") {
         rank[i][j] = "1"
       }
     }
     // 右边扩散
     if (j > 0) {
       if (copyRank[i][j - 1] === "1") {
         rank[i][j] = "1"
       }
     }
     // 左边扩散
     if (j > 0 && j < rank.length - 1) {
       if (copyRank[i][j + 1] === "1") {
         rank[i][j] = "1"
       }
     }
     // 上边扩散
     if (i > 0 && i < rank.length - 1) {
       if (copyRank[i +1][j] === "1") {
         rank[i][j] = "1"
       }
     }
   }
   
 }
 days += 1
 const set = new Set(rank.flat())
 if (set.size === 1) {
   return days
 }
 return spread(rank, JSON.parse(JSON.stringify(rank)), days)
}

const spreadTime = (str) => {
 let days = 0
 const list = str.split(",")
 const len = list.length
 const set = new Set(list)
 let rank = []
 let copyRank = []
 if (set.size === 1 && (set.has("1") || set.has("0"))) {
   days = -1
   return days
 }
 const sideLen = Math.pow(len, 0.5)
 for (let i = 0; i < sideLen; i++) {
   rank[i] = list.slice(i * sideLen, sideLen * (i + 1))
 }
 copyRank = JSON.parse(JSON.stringify(rank))
 days = spread(rank, copyRank, days)
 return days
}

// 测试用例
const result1 = spreadTime("1,1,1,1,1,1,1,1,1")
const result2 = spreadTime("1,0,1,0,0,0,1,0,1")
const result3 = spreadTime("1,0,1,0,0,0,0,0,0")
console.log(result1)
console.log(result2)
console.log(result3)

4.1 【事件推送】

/**

  • 【事件推送】分值100
  • 【事件推送】同一个数轴X上有两个点的集合A={A1,A2, …, Am}和B={B1,B2, …,Bn},Ai和Bj均为正整数,
  • A、B已经按照从小到大排好序,A、B均不为空,给定一 个距离R(正整数),
  • 列出同时满足如下条件的所有(Ai, Bj)数对:
  • 1) Ai <= Bj
  • 2) Ai, Bj之间的距离小于等于R
  • 3) 在满足1)2)的情况下,每个Ai只需输出距离最近的Bj
  • 4) 输出结果按Ai从小到大的顺序排序
  • 输入描述:
  • 第一行三个正整数m,n,R 第二行m个正整数,表示集合A 第三行n个正整数,
  • 表示集合B 输入限制: 1<=R<=100000,1<=n,m<=100000,1<=Ai,Bj<=1000000000
  • 输出描述:
  • 每组数对输出一行Ai和Bj,以空格隔开
  • 示例1:
  • 输入
  • 4 5 5
  • 1 5 5 10
  • 1 3 8 8 20
  • 输出
  • 1 1
  • 5 8
  • 5 8 */
const handleResult = (str, strA, strB) => {
  const [m, n, r] = str.split(" ")
  const listA = strA.split(" ")
  const listB = strB.split(" ")
  const result = []
  for (const a of listA) {
    let flag = true
    for (const b of listB) {
      if (parseInt(a) <= parseInt(b)) {
        const dis_ab = Math.abs(parseInt(a) - parseInt(b))
        if (dis_ab <= r && flag) {
          result.push(`${a} ${b}`)
          flag = false
        }
      }
    }
  }
  console.log(result.join("\n"))
}
// 测试用例
const str = "4 5 5"
const strA = "1 5 5 10"
const strB = "1 3 8 8 20"
handleResult(str, strA, strB)

4.2 【用户调度问题】

/**

  • 【用户调度问题】分值100
  • 【用户调度问题】在通信系统中,一个常见的问题是对用户进行不同策略的调度,会得到不同的系统消耗和性能。
  • 假设当前有n个待串行调度用户,每个用户可以使用A/B/C三种不同的调度策略,不同的策略会消耗不同的系统资源。
  • 请你根据如下规则进行用户调度,并返回总的 消耗资源数。
  • 规则:
    1. 相邻的用户不能使用相同的调度策略,例如,第1个用户使用了A策略,则第2个用户只能使用B或者C策略。
    1. 对单个用户而言,不同的调度策略对系统资源的消耗可以归一化后抽象为数值。例如,某用户分别使用A/B/C策略的系统消耗分别为15/8/17。
    1. 每个用户依次选择当前所能选择的对系统资源消耗最少的策略(局部最优),如果有多个满足要求的策略,选最后一个。
  • 输入描述:
  • 第一行表示用户个数n
  • 接下来每一行表示一个用户分别使用三个策略的系统消耗resA resB resC
  • 输出描述:
  • 最优策略组合下的总的系统资源消耗数
  • 备注:
  • 所有策略对系统的资源消耗均为正整数,n < 1000
  • 示例1:
  • 输入
  • 3
  • 15 8 17
  • 12 20 9
  • 11 7 5
  • 输出
  • 24 */
/**
 * 递归方法遍历数组
 * @param arr       数组
 * @param duplicate 上次出现的位置
 * @param leftCount 剩余递归次数
 * @param total 记录当前和
 * @param recursionCount 已递归次数
 * @param result 记录当前和的数组 
 * @return
 */
const recursion = (arr, duplicate, leftCount, total, recursionCount, result) => {
  let left = leftCount - 1;
  if (left == 0) {
      result.push(total)
      return;
  }
  if (duplicate == 0) {
      recursion(arr, 1, left, total + arr[recursionCount][1], recursionCount + 1, result)
      recursion(arr, 2, left, total + arr[recursionCount][2], recursionCount + 1, result)
  } else if (duplicate == 1) {
      recursion(arr, 0, left, total + arr[recursionCount][0], recursionCount + 1, result)
      recursion(arr, 2, left, total + arr[recursionCount][2], recursionCount + 1, result)
  } else if (duplicate == 2) {
      recursion(arr, 0, left, total + arr[recursionCount][0], recursionCount + 1, result)
      recursion(arr, 1, left, total + arr[recursionCount][1], recursionCount + 1, result)
  }
}
const sum = (arr, num) => {
  const result = []
  recursion(arr, 0, num, arr[0][0], 1, result)
  recursion(arr, 1, num, arr[0][1], 1, result)
  recursion(arr, 2, num, arr[0][2], 1, result)
  result.sort((a, b) => a - b)
  console.log(result[0])
}

// 测试用例
// const num = parseInt(readline());
// const arr = []
// for (let i = 0; i < num; i++) {
//   const res = (readline().split(" ")).map(item => parseInt(item))
//   arr[i] = res
// }

/**
 * 3
 * 15 8 17
 * 12 20 9
 * 11 7 5
 */

const num = 3
// arr构造成二位数组
const arr = [[15, 8, 17], [12, 20, 9], [11, 7, 5]]
// const num = 9
// const arr = [[7,15,14],[17,15,19],[7,20,26],[23,25,6],[8,27,25],[2,12,6],[6,30,22],[8,2,13],[17,1,26]]
sum(arr, num)

5.1 【高矮个子排队】

/**

  • 【高矮个子排队】分值100
  • 现在有一队小朋友,他们高矮不同,我们以正整数数组表示这一队小朋友的身高,如数组{5,3,1,2,3}
  • 我们现在希望小朋友排队,以“高”“矮”“高”“矮”顺序排列,每一个“高”位置的小朋友要比相邻的位置高或者相等;每一个“矮”位置的小朋友要比相邻的位置矮或者相等
  • 要求小朋友们移动的距离和最小,第一个从“高”位开始排,输出最小移动距离即可。
  • 例如,在示范小队{5,3,1,2,3}中,{5, 1, 3, 2, 3}是排序结果。{5, 2, 3, 1, 3} 虽然也满足“高”“矮”“高”“矮”顺序排列,但小朋友们的移动距离大,所以不是最优结果。
  • 移动距离的定义如下所示:
  • 第二位小朋友移到第三位小朋友后面,移动距离为1,若移动到第四位小朋友后面,移动距离为2;
  • 示例1:
  • 输入
  • 4 1 3 5 2
  • 输出
  • 4 1 5 2 3 */
/ 交换数组中两个数的位置
const swap = (arr, i, j) => {
  const temp = arr[i]
  arr[i] = arr[j]
  arr[j] = temp
}

const handleMovePosition = (str) => {
  const arr = str.split(" ")
  //使用双指针,当前指向和下一个进行比较,如果是偶数下标就判断是否大,奇数判断是否小
  let cur = 0
  let next = cur + 1
  while(next !== arr.length) {
    if (cur % 2 === 0) {
      if (arr[cur] < arr[next]) {
        swap(arr, cur, next)
      }
    } else {
      if (arr[cur] > arr[next]) {
        swap(arr, cur, next)
      }
    }
    cur++
    next++
  }
  return arr.join(" ")
}

// 测试用例
const str ="4 1 3 5 2"
// const str1 = "5 3 1 2 3"
const result = handleMovePosition(str)
console.log(result)

5.2 【第k个排列】

/**

  • 【第k个排列】分值100
  • 【第k个排列】给定参数n,从1到n会有n个整数:1,2,3,…,n,这n个数字共有 n! 种排列。按大 小顺序升序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
  • "123" "132" "213" "231" "312" "321"
  • 给定 n 和 k,返回第 k 个排列。
  • 输 入描述:
  • 输入两行,第一行为n,第二行为k,给定 n 的范围是 [1,9],给定 k 的范围是[1,n!]。
  • 输出描述:
  • 输出排在第k位置的数字。
  • 示例1:
  • 输入
  • 3
  • 3
  • 输出
  • 213 */
const getPermutation = (n, k) => {
  const num = []
  const factorial = [1]
  let str = ""
  for (let i = 1; i <= n; i++) {
    num.push(i)
  }
  for (let i = 1; i < n; i++) {
    factorial[i] = i * factorial[i - 1]
  }
  n--
  while(n >= 0) {
    const t = factorial[n]
    let loc = parseInt(Math.ceil(k / t)) - 1
    if (loc === -1) {
      loc = num.length - 1
    }
    str += num[loc]
    num.splice(loc, 1)
    k %= t
    n--
  }
  return str
}
// 测试用例
const result = getPermutation(3, 3)
console.log(result)

5.3 【最长的指定瑕疵度的元音子串】

/**

  • 【最长的指定瑕疵度的元音子串】分值200
  • 【最长的指定瑕疵度的元音子串】开头和结尾都是元音字母(aeiouAEIOU)的字符串为 元音字符串 ,其中混杂的非元音字母数量为其 瑕疵度 。
  • 比如: · “a” 、 “aa”是元音字符串,其瑕疵度都为0 · “aiur”不是元音字符串(结尾不是元音字符) · “abira”是元音字符串,其瑕疵度为2
  • 给定一个字符串,请找出指定瑕疵度的最长元音字符子串,并输出其长度,如果找不到满足条件的元音字符子串,输出0。子串: 字符串中任意个连续的字符组成的子序列称为该字符串的子串.
  • 输入描述:
  • 首行输入是一个整数,表示预期的瑕疵度flaw,取值范围[0, 65535]。 接下来一行是一个仅由字符a-z和A-Z组成的字符串,字符串长度(0,
  • 输出描述:
  • 输出为一个整数,代表满足条件的元音字符子串的长度。
  • 示例1:
  • 输入
  • 0
  • asdbuiodevauufgh
  • 输出
  • 3 */
const getLongestFlawedVowelSubstrLen = (flaw, input) => {
  input = input.toLowerCase()
  //利用hash算法确定是否为元音
  let au = {
      a: 1,
      e: 1,
      i: 1,
      o: 1,
      u: 1
  }
  let left = 0
  let right = 0
  let arr = []
  let count = 0
  //每次循环右指针向右进一格,直到遍历完数组
  for (right = 0; right < input.length; right++) {
      //利用哈希算法判断右指针是否为元音,即子串结尾要求时元音
      let isRightYuan = au[input[right]] === 1 ? true : false
      if (isRightYuan === false) {
          count++
      }
      //要求足够的瑕疵度
      if (isRightYuan === false || count < flaw) {
          continue
      }
      //足够的瑕疵度,且左边的也是元音,则记录该子串
      if (count === flaw && au[input[left]] === 1) {
          let temp = input.substring(left, right + 1)
          arr.push(temp)
          continue
      }
      //当瑕疵度大于要求的瑕疵度或子串左边非元音时,需要移动左指针
      while (count > flaw || au[input[left]] !== 1) {
          left++
          if (au[input[left - 1]] !== 1) {
              count--
          }
          if (left > right) {
              left = right
              break
          }
      }
      //记录跳出循环时是否符合要求
      if (count == flaw && au[input[left]] === 1) {
          let temp = input.substring(left, right + 1)
          arr.push(temp)
      }
  }
  if (arr.length > 1) {
      //按长度大小给子串排序
      arr.sort((a, b) => b.length - a.length)
      if (arr.length > 0) {
          return arr[0].length
      }
  } else if (arr.length === 1) {
      return arr[0].length
  } else {
      return 0
  }
}
console.log(getLongestFlawedVowelSubstrLen(0, 'asdbuiodevauufgh'))

6.1【最长连续子序列】分值100

/**

  • 【最长连续子序列】分值100
  • 有N个正整数组成的一个序列。给定整数sum,求长度最长的连续子序列,使他们的和等于sum,返回此子序列的长度,如果没有满足要求的序列,返回-1。
  • 输入描述:
  • 序列:1,2,3,4,2
  • sum:6
  • 输出描述:
  • 序列长度:3
  • 备注:
  • 输入序列仅由数字和英文逗号构成,数字之间采用英文逗号分隔; 序 列长度:1 <= N <= 200; 输入序列不考虑异常情况,由题目保证输入序列满足要求。
  • 示例1:
  • 输入
  • 1,2,3,4,2 6
  • 输出
  • 3 */
const handleResult = (str, sum) => {
 const arr = str.split(',').map(item => parseInt(item))
 let temp = 0
 let res = []
 let maxLen = -1
 for (let i = 0; i < arr.length; i++) {
   temp += arr[i]
   res.push(arr[i])
   for (let j = i + 1; j < arr.length; j++) {
     temp += arr[j]
     res.push(arr[j])
     if (temp > sum) break
     if (temp === sum) {
       maxLen =Math.max(res.length, maxLen)
       break
     }
     
   }
   temp = 0
   res = []
   
 }
 return maxLen
}

// 测试用例

const res = handleResult("1,2,3,5,3", parseInt("6"))
console.log(res)

6.2 【滑动窗口最大和】分值100

/**
 * 【滑动窗口最大和】分值100
 * 【滑动窗口最大和】有一个N个整数的数组,和一个长度为M的窗口,窗口从数组内的第一个数开始滑动直到窗口不能滑动为止,每次窗口滑动产生一个窗口和(窗 口 内所有数的和),求窗口滑动产生的所有窗口和的最大值。
 * 输入描述:
 *  第一行输入一个正整数N,表示整数个数。(0<N<100000) 第 二行输入N个整数,整数的* 取值范围为[-100,100]。 第三行输入一个正整数M,M代表窗口大小,M<=100000,且* * M<=N。
 * 输出描述:
 *  窗口滑动产生的所有窗口和的最大值。
 * 示例1:
 *  输入
 *   6
 *   10 20 30 15 23 12 
 *   3
 *  输出
 *   68
 */ 
const slideWindowMax = (n, str, m) => {
  const arr = str.split(' ').map(item => parseInt(item))
  let maxEle = 0
  for (let i = 0; i < arr.length; i++) {
    let sum = 0
    if ((n - i) < m) break
    for (let j = i; j < m + i; j++) {
      sum += arr[j]
      
    }
    maxEle = Math.max(maxEle, sum)
    
  }
  return maxEle
}

// 测试用例

const res = slideWindowMax(parseInt("6"), "10 20 30 15 23 12", parseInt("3"))
console.log(res)

7.1 【最远足迹】分值100

/**
 * 【最远足迹】分值100
 * 某探险队负责对地下洞穴进行探险。探险队成员在进行探险任务时,随身携带的记录器会不定期地记录自身的坐标,但在记录的间隙中也会记录其他数据。
 * 探索工作 结束后,探险队需要获取到某成员在探险过程中相对于探险队总部的最远的足迹位置。 
 * 1. 仪器记录坐标时,坐标的数据格式为(x,y),如(1,2)、(100,200),其中0<x<1000,0<y<1000。同时存在非法坐标,如(01,1)、(1,01),(0,100)属于非法坐标。 
 * 4. 设定探险队总部的坐标为(0,0),某位置相对总部的距离为:x*x+y*y。 
 * 5. 若两个座标的相对总部的距离相同,则第一次到达的坐标为最远的足迹。 
 * 6. 若记录仪中的坐标都不合法,输出总部坐标(0,0)。
 * 备注:不需要考虑双层括号嵌套的情况,比如sfsdfsd((1,2))。 
 * 输入描述:
 *  字符串,表示记录仪中的数据。 如:ferga13fdsf3(100,200)f2r3rfasf(300,400)
 * 输出描述: 
 *  字符串,表示最远足迹到达的坐标。如: (300,400)
 * 示例1:
 *   输入
 *     ferg(3,10)a13fdsf3(3,4)f2r3rfasf(5,10)
 *   输出
 *     (5,10)
 */
const handleResult = (str) => {
  let max = 0
  let ans = "(0,0)"
  const left = []
  const right = []
  for (let i = 0; i < str.length; i++) {
    if (str[i] === '(') {
      left.push(i)
    }
    if (str[i] === ')') {
      right.push(i)
    }
    
  }
  for (let i = 0; i < left.length; i++) {
    const s = str.slice(left[i] + 1, right[i]).split(',')
    if (s[0].charAt(0) !== '0' && s[1].charAt(0) !== '0') {
      const num1 = parseInt(s[0])
      const num2 = parseInt(s[1])
      if (num1 <1000 && num2 < 1000 && num1 * num1 + num2 * num2 > max) {
        max = num1 * num1 + num2 * num2
        ans = `(${s[0]},${s[1]})`
      }
    }
    
  }
  return ans
}

// 测试用例

const str = "ferg(3,10)a13fdsf3(3,4)f2r3rfasf(5,10)"
const result = handleResult(str)
console.log(result)

7.2 【矩形相交的面积】分值100

/**
 * 【矩形相交的面积】分值100
 * 【矩形相交的面积】 在坐标系中,给定3个矩形,求相交区域的面积。
 * 输入描述:
 * 3行输入分别为3个矩形的位置,分别代表 ‘左上角x坐标’,‘左上角y坐标’,‘矩形宽’,‘矩形高’ -1000 <= x,y < 1000
 * 输出描述:
 * 输出3个矩形相交的面积,不相交的输出0
 * 示例1:
 * 输入
 * 1 6 4 4 
 * 3 5 3 4 
 * 0 3 7 3
 * 输出
 * 2
 * 
 */
let recA = {x:1, y:6, w:4, h:4}
let recB = {x:3, y:5, w:3, h:4}
let recC = {x:0, y:3, w:7, h:3}

const printPrinter = (recA, recB) => {
  let recLeft = null
  let recRight = null
  let recTop = null
  let recBottom = null
  // 找到左边矩形和右边的矩形
  if (recA.x < recB.x) {
    recLeft = recA
    recRight = recB
  } else {
    recRight = recA
    recLeft = recB
  }
  // 如果左边的矩形x坐标 + 宽都没大于右边的矩形,说明不想交,如果两个矩形不想交,那么肯定没有三个矩形的公共部分返回-1
  if(recLeft.x + recLeft.w <= recRight.x) {
    return {x:-1, y:-1, w:-1, h:-1}
  }
  // 同理找到上面的矩形和下面的矩形
  if(recA.y < recB.y) {
    recTop = recB
    recBottom = recA
  } else {
    recTop = recA
    recBottom = recB
  }
  // 如果下边的矩形y左边+高都没大于上班的矩形,说明不想交
  if (recBottom.y + recBottom.h <= recTop.x) {
    return {x:-1, y:-1, w:-1, h:-1}
  }

  return {
    x: recRight.x,
    y: recRight.y,
    w: (recLeft.x + recLeft.w) < (recRight.x + recRight.w) ? (recLeft.x + recLeft.w - recRight.x) : (recRight.x + recRight.w - recRight.x),
    h: (recBottom.y + recBottom.h) < (recTop.y + recTop.h) ? (recBottom.y +recBottom.h - recTop.y) : (recTop.y + recTop.h - recTop.y)
  }
}

const res = printPrinter(recA, recB)
const result = printPrinter(res, recC)
if (result.x === -1) {
  console.log(0)
} else {
  console.log(result.w * result.h)
}

7.3 【高效的任务规划】

/**
 * 【高效的任务规划】 你有n台机器编号为1~n,每台都需要完成完成一项工作,机器经过配置后都能完成独立完成一项工作。
 * 假设第i台机器你需要花Bi分钟进行设置,然后开始运 行,Ji分钟后完成任务。现在,你需要选择布置工作的顺序,使得用最短的时间完成所有工作。
 * 注意,不能同时对两台进行配置,但配置完成的机器们可以同时执行 他 们各自的工作。 
 * 输入描述:
 * 第一行输入代表总共有M组任务数据(1 < M <= 10)。 每组数第一行为一个整数指定机器的数量N(0 < N <= 1000)。
 * 随后的N行每行两个整数,第一个表示B(0 <= B <=10000),第二个表示J(0 <= J <= 100 00)。 每组数据连续输入,不会用空行分隔。各组任务单独计时。
 * 输出描述:
 * 对于每组任务,输出最短完成时间,且每组的结果独占一行。例如,两组任务就应该有两行输出。
 * 示例1:
 * 输入
 * 1 
 * 1
 * 2 2
 * 输出
 * 4
 */
const assignmentArrangement = () => {
  const m = parseInt(readline())
  for (let i = 0; i < m; i++) {
    const n = parseInt(readline())
    let time = 0
    let last = 0
    let res = 0
    const machine = []
    for (let j = 0; j < n; j++) {
      const b = parseInt(readline().split(" ")[0])
      const k = parseInt(readline().split(" ")[1])
      machine[i][0] = b
      machine[i][1] = k    
    }
    machine.sort((a, b) => b - a)
    for (let l = 0; l < n; l++) {
      time += last + machine[i][0] + machine[i][1]
      last += machine[i][0]
      res = Math.max(res, time)
      
    }
    console.log(res)  
  }
}

assignmentArrangement()

8.1 【报数游戏】分值100

/**
 * 【报数游戏】分值100
 * 【报数游戏】100个人围成一圈,每个人有一个编码,编号从1开始到100。他们从1开始依次报数,报到为M的人自动退出圈圈,然后下一个人接着从1开始报数, 直到 剩余的人数小于M。请问最后剩余的人在原先的编号为多少?
 *  输入描述:
 * 输入一个整数参数M
 * 输出描述:
 * 如果输入参数M小于等于1或者大于等于100,输出“ERROR!”;否则按照原先的编号从小到大的顺序,以英文逗号分割输出编号字符串
 * 示例1:
 * 输入
 * 3
 * 输出
 * 58,91
 * 
 */
const handleResult = m => {
  const person = 100
  const arr = []
  let index = 0
  for (let i = 1; i <= person; i++) {
    arr.push(i)
    
  }
  while(arr.length >= m) {
    index = (index +m -1)% arr.length
    arr.splice(index, 1)
  }
  console.log(arr)
}

handleResult(3)

8.2篮球比赛】分值200

/**
 * 【篮球比赛】分值200
 * 【篮球比赛】篮球(5V5)比赛中,每个球员拥有一个战斗力,每个队伍的所有球员战斗力之和为该队伍的总体战斗力。现有10个球员准备分为两队进行训练赛,教 练 希望2个队伍的战斗力差值能够尽可能的小,以达到最佳训练效果。
 * 给出10个球员的战斗力,如果你是教练,你该如何分队,才能达到最佳训练效果?请输出该分 队 方案下的最小战斗力差值。
 * 输入描述:
 * 10个篮球队员的战斗力(整数,范围[1,10000]),战斗力之间用空格分隔,如:10 9 8 7 6 5 4 3 2 1 不 需要考虑异常输入的场景。
 * 输出描述:
 * 最小的战斗力差值,如:1
 * 示例1:
 * 输入
 * 10 9 8 7 6 5 4 3 2 1
 * 输出
 * 1
 */
const getSum = arr => {
  const sum = arr.reduce((pre, cur) => {
    return pre + cur
  }, 0)
  return sum
}

const getMinusArray = (a, b) => {
  const suma = getSum(a)
  const sumb = getSum(b)
  let startMinus = Math.abs(suma - sumb)
  let minus = 0
  for (let i = 0; i < a.length; i++) {
    for (let j = 0; j < a.length; j++) {
      const temp = a[i]
      a[i] = b[j]
      b[j] = temp
      minus = Math.abs(getSum(a) - getSum(b))
      if (minus < startMinus) {
        startMinus = minus
      } else {
        const temp = a[i]
        a[i] = a[j]
        a[j] = temp
      }
      
    }
    
  }
  console.log(startMinus)
}

const handleResult = str => {
  const arr = str.split(" ").map(item => parseInt(item))
  const a = arr.slice(0, arr.length/2)
  const b = arr.slice(arr.length/2)
  getMinusArray(a, b)
  return Math.abs(getSum(a) - getSum(b))
}

handleResult("10 9 8 7 6 5 4 3 2 1")

10.1 【工号不够用了怎么办】分值100

/**
 * 【工号不够用了怎么办】分值100
 * 【工号不够用了怎么办?】3020年,空间通信集团的员工人数突破20亿人,即将遇到现有工号不够用的窘境。 
 * 现在,请你负责调研新工号系统。继承历史传统,新的工号系统由小写英文字母(a-z)和数字(0-9)两部分构成。
 * 新工号由一段英文字母开头,之后跟随一段数字, 比如"aaahw0001","a12345","abcd1","a00"。注意新工号不能全为字母或者数字,允许数字部分有前导0或者全为0。 
 * 但是过长的工号会增加同事们的记忆成本,现在给出新工号至少需要分配的人数X和新工号中字母的长度Y,求新工号中数字的最短长度Z。
 * 输入描述:
 * 一行两个非负整数 X Y,用数字用单个空格分 隔。0< X <=2^50 - 1 0< Y <=5
 * 输出描述:
 * 输出新工号中数字的最短长度Z
 * 示例1:
 * 输入
 * 260 1
 * 输出
 * 1
 */
const getWorkNum = (total, charNum) => {
  if (total <= 26) {
    return 1
  }
  let del = 1
  for (let i = 0; i < charNum; i++) {
    del*=26
    
  }
  let r = 1
  del*= 10
  while(del < total) {
    del *= 10
    r++
  }
  return r
}

const result = getWorkNum(260, 1)
console.log(result)

11.1【找车位】分值100

/**
 * 【找车位】分值100
 * 【找车位】停车场有一横排车位,0代表没有停车,1代表有车。至少停了一辆车在车位上,也至少有一个空位没有停车。
 *  为了防剐蹭,需为停车人找到一个车位,使得距停车人的车最近的车辆的距离是最大的,返回此时的最大距离
 * 输入描述:
 * 1、一个用半角逗号分割的停车标识字符串,停车标识为0或1,0为空位,1为已停车。 2、停车位最多100个。
 * 输出描述:
 * 输出一个整数记录最大距离。
 * 示例1:
 * 输入
 * 1,0,0,0,0,1,0,0,1,0,1
 * 输出
 * 2
 * 
 */
const test = (param) => {
  const arr = param.split(",")
  let max = 0
  for (let i = 0; i < arr.length; i++) {
    const s = arr[i]
    if (s == '0') {
      let after = arr.indexOf('1', i) // 从i往后查找1的位置
      if (after == -1) {
        after = arr.length - 1
      }
      let pre = arr.lastIndexOf('1', i) // 从i往前查找1的位置
      if (pre == -1) {
        pre = 100
      }
      let min = Math.min(after - i, i - pre)
      max = Math.max(min, max)
    }
    
  }
  return max
}

const result = test("1,0,0,0,0,1,0,0,1,0,1")
console.log(result)

18.1【最长的顺子】

/**
 * 【最长的顺子】 斗地主起源于湖北十堰房县,据传是一位叫吴修全的年轻人根据当地流行的扑克玩法“跑得快”改编的,如今已风靡整个中国,并流行于互联网上。
 *  牌型: 单顺, 又称顺子,最少5张牌,最多12张牌(3⋯ A),不能有2,也不能有大小王,不计花色 
 * 例如:3-4-5-6-7-8,7-8-9-10-J-Q,3-4-5-6-7-8-9-10-J-Q-K-A 可用的牌 3<4<5<6<7<8<9<10<J<Q<K<A<2 < B(小王)< C(大王),每种牌除大小王外有4种花色(共有 13X4 + 2 张牌) 
 * 输入1. 手上已有的牌 2. 已经出过的牌(包括对手出的和自己出的牌) 输出: 对手可能构成的最长的顺子(如果有相同长度的顺子, 输出牌面最大的那一个),如果无法构成顺子, 则输出 NO-CHAIN
 * 输入描述:
 * 输入的第一行为当前手中的牌 输入的第二行为已经出过的牌
 * 输出描述:
 * 最长的顺子
 * 示例1:
 * 输入
 * 3-3-3-3-4-4-5-5-6-7-8-9-10-J-Q-K-A 
 * 4-5-6-7-8-8-8
 * 输出
 * 9-10-J-Q-K-A
 */
function maxShunzi (own, pubilc) {
    let known = own.split('-').concat(pubilc.split('-'));
    let rivalObj = {3:4,4:4,5:4,6:4,7:4,8:4,9:4,10:4,J:4,Q:4,K:4,A:4};
    let arr = [];
    for (let i = 0; i < known.length; i++) {
        if (rivalObj[known[i]]) {
            rivalObj[known[i]]--;
        }
    }
    let i = 0;
    let newarr = [];
    for (let item in rivalObj) {
        if(rivalObj[item] != 0) {
            newarr.push(item);
        } else {
            arr.push(newarr)
            newarr = [];
        }
    };
    arr.push(newarr);
    let maxlenth = 0;
    for(let i =0; i<arr.length;i++) {
        if(arr[i].length >= maxlenth) {
            maxlenth = i;
        }
    }
    console.log(arr[maxlenth].join('-'))
}
let startTime = Date.now();
maxShunzi('3-3-3-3-4-4-5-5-6-7-8-9-10-J-Q-K-A', '4-5-6-7-8-8-8');
let endTime = Date.now();
console.log("用时:", endTime - startTime, 'ms');

18.2 【找终点】

/**
 * 【找终点】 给定一个正整数数组,设为nums,最大为100个成员,求从第一个成员开始,正好走到数组最后一个成员,所使用的最少步骤数。
 * 要求: 1、第一步必须从第一元素开始,且1<=第一步的步长<len/2;(len为数组的长度,需要自行解析)。 
 * 2、从第二步开始,只能以所在成员的数字走相应的步数,不能多也不能少, 如果目标不可达返回-1,只输出最少的步骤数量。 3、只能向数组的尾部走,不能往回走。
 * 输入描述:
 * 由正整数组成的数组,以空格分隔,数组长度小于100,请自行解析数据数量。
 * 输出描述:
 * 正整数,表示最少的步数,如果不存在输出-1
 * 示例1:
 * 输入
 * 7 5 9 4 2 6 8 3 5 4 3 9
 * 输出
 * 2
 * 
 */
function findLast(str) {
    if (!str) {
        console.log(-1);
        return;
    }
    let arr = str.split(' ');
    let findFlag = false;
    let result = 101;
    if (arr[0] == arr.length - 1) {
        result = 1;
        console.log(result);
        return;
    }
    for (let i = 0; i < arr.length/2; i++) {
        let step = 1;
        let cur = i;
        while (cur < arr.length-1) {
            cur += +arr[cur];
            step++;
        }
        if (cur == arr.length-1) {
            findFlag = true;
            if (step < result) {
                result = step;
            }
        }
    }
    if (findFlag) {
        console.log(result);
    } else {
        console.log(-1);
    }
}
let startTime = Date.now();
findLast('7 5 9 4 2 6 8 3 5 4 3 9');
// findLast('1 2 3 4 5 2 5 6 7 9 4 2 6 8 3 5 4 3 9');
// findLast('9 4 2 6 8 3 5 4 3 9');
// findLast('9 4 10 12');
// findLast('1 2 4 2 1 1 100 120 1 2 3 4');
// findLast('1 1');
// findLast('');
let endTime = Date.now();
console.log("用时:", endTime - startTime, 'ms');

19.1 【5键键盘的输出】

/**
 * 【5键键盘的输出】有一个特殊的5键键盘,上面有a,ctrl-c,ctrl-x,ctrl-v,ctrl-a五个键。a键在屏幕上输出一个字母a;
 * ctrl-c将当前选择的字母复制到剪贴 板; ctrl-x将当前选择的字母复制到剪贴板,并清空选择的字母;ctrl-v将当前剪贴板里的字母输出到屏幕;
 * ctrl-a选择当前屏幕上的所有字母。注意: 剪贴板初始为空,新的内容被复制到剪贴板时会覆盖原来的内容 当屏幕上没有字母时,ctrl-a无效 当没有选择字母时,
 * ctrl-c和ctrl-x无效 当有字母被选择时,a和ctrl-v这两个有输出功能的键会先清空选择的字母,再进行输出 给定一系列键盘输入,输出最终屏幕上字母的数量
 * 输入描述:
 * 输入为一行,为简化解析,用数字1 2 3 4 5代表a,ctrl-c,ctrl-x,ctrl-v,ctrl-a五个键的输入,数字用空格分隔
 * 输出描述:
 * 输出一个数字,为最终屏幕上字母的数量
 * 示例1:
 * 输入
 * 1 1 1
 * 输出
 * 3
 * 
 */
function keybordOutput (input) {
    let inputArr = input.split(" ");
    let clipboard = 0;
    let isSelectedAll = false;
    let outPutArr = 0;
    for (item of inputArr) {
        if (item == 1) {
            if (isSelectedAll) {
                outPutArr = 1;
                isSelectedAll = false;
            } else {
                outPutArr++;
            }
        } else if (item == 2 && isSelectedAll) {
            clipboard = outPutArr;
        } else if (item == 3 && isSelectedAll) {
            clipboard = outPutArr;
            outPutArr = 0;
            isSelectedAll == false;
        } else if (item == 4) {
            if (isSelectedAll) {
                outPutArr = clipboard;
                isSelectedAll = false;
            } else {
                outPutArr += clipboard;
            }
        } else if (item == 5) {
            isSelectedAll = true;
        }
    }
    console.log(outPutArr);
}
keybordOutput('1 1 1 5 2 4 4 1');

19.2 【滑动窗口最大和】

/**
 * 【滑动窗口最大和】有一个N个整数的数组,和一个长度为M的窗口,窗口从数组内的第一个数开始滑动直到窗口不能滑动为止,每次窗口滑动产生一个窗口和 (窗口内所有数的和),求窗口滑动产生的所有窗口和的最大值。
 * 输入描述:
 * 第一行输入一个正整数N,表示整数个数。(0<N<100000) 第二行输入N个整数,整数的取值范围为[-100,100]。 第三行输入一个正整数M,M代表窗口大小,M<=100000,且M<=N。
 * 输出描述:
 * 窗口滑动产生的所有窗口和的最大值。
 * 示例1:
 * 输入
 * 6
 * 10 20 30 15 23 12 
 * 3
 * 输出
 * 68 
 */
function maxSum (num, arr, size) {
    if(size > num || !num || !size) {
        return;
    } else if (size == num) {
        let max = arr.split(' ').reduce((pre, cur) => {
            return parseInt(pre) + parseInt(cur);
        })
        console.log(max);
        return;
    }
    let numArr = [];
    for (let i = 0; i < num - size; i++) {
        let maxNum = 0;
        for(let j = 0; j < size; j++) {
            maxNum += +arr.split(" ")[i + j];
        }
        numArr.push(maxNum);
    }
    let max = numArr.sort()[numArr.length-1];
    console.log(max);
}
let startTime = Date.now();
maxSum(6, '10 20 30 15 23 12', 3);
let endTime = Date.now();
console.log("用时:", endTime - startTime, 'ms');

20.1 【字符串变换最小字符串】

/**
 * 【字符串变换最小字符串】 给定一个字符串s,最多只能进行一次变换,返回变换后能得到的最小字符串(按照字典序进行比较)。变换规则:交换字符串中任意两个不同位置的字符。
 * 输入描述:
 * 一串小写字母组成的字符串s
 * 输出描述:
 * 按照要求进行变换得到的最小字符串
 * 备注:
 * s是都是小写字符 组成1<=s.length<=1000
 * 示例1:
 * 输入
 * abcdef
 * 
 * 输出
 * abcdef
 */
let str = 'abcdefga';
let arr = str.split('')
function sortmin(array) {
    let min = 0;
    for(let i = 0; i < array.length; i++) {
        for (let j = i+1;j< array.length;j++){
            if(array[j] < array[i]) {
                let temp = array[i];
                array[i] = array[j];
                array[j] = temp;
                console.log(array.join(''))
                return;
            }
        }
    }
}
sortmin(arr);

20.2 【判断字符串子序列】

/**
 * 【判断字符串子序列】给定字符串 target和 source, 判断 target 是否为 source 的子序列。
 *  你可以认为 target 和 source 中仅包含英文小写字母。字符串 source可能会很长(长度 ~= 500,000),而 target 是个短字符串(长度 <=100)。
 *  字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"abc""aebycd"的一个子序列, 而"ayb"不是)。 请找出最后一个子序列的起始位置。 
 * 输入描述:
 * 第一行为target,短字符串(长度 <=100) 第二行为source,长字符串(长度 ~= 500,000)
 * 输出描述:
 * 最后一个子序列的起始位置, 即最后一个子序列首字母的下标
 * 备注:
 * 若在source中找不到target,则输出-1
 * 示例1:
 * 输入
 * abc 
 * abcaybec
 * 输出
 * 3
 */
function sonStr(target, source) {
    let curindex = source.length - 1;
    for(let i = target.length - 1; i >= 0; i--) {
        for(let j = curindex; j >= 0; j--){
            if (source[j] == target[i]) {
                curindex = j;
                break;
            } else {
                curindex = -1;
            }
        }
    }
    console.log(curindex);
}
sonStr('abc', 'abcaybec');