算法学习记录(四十二)

148 阅读3分钟

问:

  1. 一个长度为N的数组,代表有N条鱼,每条鱼体积为arr[i],每一轮中左边的大鱼一定会吃掉右边比自己小的第一条鱼。并且每条鱼吃掉比自己小的鱼是同时发生的,多少轮后鱼的数量会稳定。比如:6 6 3 3 。第一轮两个6都会选择吃第一个3。变成 6 6 3。第二轮: 6 6 ,此时稳定。
  2. 到达终点数字
  3. 假设有一个长度为26的数组。元素是a ~ z。对这个数组的所有子序列按照如下规定:1:a,2:b,3:c···26:z,27:ab,28:ac···。前面的数字代表这个子序列的编号。给任意一个子序列,请返回它的编号

解:

  1. 倒序遍历数组,创建单调栈。
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
    }
}