问:
- 一个长度为N的数组,代表有N条鱼,每条鱼体积为arr[i],每一轮中左边的大鱼一定会吃掉右边比自己小的第一条鱼。并且每条鱼吃掉比自己小的鱼是同时发生的,多少轮后鱼的数量会稳定。比如:6 6 3 3 。第一轮两个6都会选择吃第一个3。变成 6 6 3。第二轮: 6 6 ,此时稳定。
- 到达终点数字
- 假设有一个长度为26的数组。元素是a ~ z。对这个数组的所有子序列按照如下规定:1:a,2:b,3:c···26:z,27:ab,28:ac···。前面的数字代表这个子序列的编号。给任意一个子序列,请返回它的编号
解:
- 倒序遍历数组,创建单调栈。
function getTimes(arr) {
const helpArr = []
let maxTimes = 0
for (let i = arr.length - 1; i >= 0; i--) {
const node = {
val: arr[i],
times: 0
}
if (!helpArr.length) {
helpArr.push(node)
continue
}
// 若当前遍历到的节点,比单调栈最后一个值大。
while (helpArr[helpArr.length - 1]?.val < node.val) {
// 弹出一个节点,并且更新当前节点
const last = helpArr.pop()
node.times = Math.max(node.times + 1, last.times)
}
helpArr.push(node)
maxTimes = Math.max(maxTimes, node.times)
}
return maxTimes
}
function reachNumber(target) {
let idx = 0
let curNum = 0
// 正负结果一样,所以直接当正数来看
target = Math.abs(target)
while (curNum < target) {
curNum += idx
idx++
}
// 此时curNum第一次大于等于target
// 如果等于,直接返回次数
if (curNum === target) return idx - 1
// 如果相差是个偶数,说明在某一步的时候反向走一次,最终结果可以达到target
if ((curNum - target) % 2 === 0) {
return idx - 1
}
// 如果是个奇数。再往后走看看
while ((curNum - target) % 2 !== 0) {
curNum += idx
idx++
}
return idx - 1
}
// 精简代码
function reachNumber(target) {
let idx = 0
let curNum = 0
target = Math.abs(target)
while (curNum < target || (curNum - target) % 2 !== 0) {
curNum += idx
idx++
}
return idx - 1
}
function getOrder(target) {
const dict = ['a','b','c','d','e','f','g','h','i','g','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
const len = target.length
let num = 0
// 所有长度比target小的都排在target前面
let preLen = len - 1
while (preLen) {
num += getAllLength(preLen--)
}
// 若target长度为4,那么此时num等于长度为1,2,3的子序列总和
// 假设target='dfgj' 需要再计算以a/b/c开头长度为4的子序列总和,这些都排在d开头长度为4的子序列前面
let idx = 0
let preCharIdx = 0
while (idx <= len) {
// 当前位置字符,第一次来到d
const curChar = target[idx]
// d的位置
const charIdx = dict.indexOf(curChar)
// 记录上一个字符的位置,作为下一个字符遍历的起点,譬如dfgj 在f字符时,不能找d之前的字符,因为不是子序列了
preCharIdx = charIdx
// 所有以在d之前字符为开头,长度为4的子序列总和
for (let i = preCharIdx; i < charIdx; i++) {
num += getStartLength(dict[i], len - idx)
}
// 第一个字符计算完了,就去下一个字符计算。也就是说到了f字符。找所有以在f之前字符为开头,长度为3的子序列总和。计算完了再下一个
// 直到idx越界,计算完毕
idx++
}
return num + 1
// 计算所有以start开头,长度为length的子序列有多少个
function getStartLength(start, length) {
if (length <= 1) return length
let sum = 0
const curIdx = dict.indexOf(start)
for (let i = curIdx + 1; i <= 26; i++) {
sum += getStartLength(dict[i], length - 1)
}
return sum
}
// 计算所有长度为length的子序列有多少个
function getAllLength(length) {
let sum = 0
for (let i = 0; i <= 26; i++) {
sum += getStartLength(dict[i], length)
}
return sum
}
}