算法学习记录(四十一)

114 阅读1分钟

问:

  1. 给定一个只由0,1,&,|,^组成的字符串str,给再给一个布尔值flag。返回str形成的布尔值等于flag的组合方式的个数
  2. 最长无重复字符子串长度
  3. 编辑距离
  4. 给定一个全是小写字母的str。删除多余字符,使得每种字符只保留一个。并且让最终结果的字典序最小。譬如:str=‘acbc’删除第一个‘c’得到‘abc’比删除第二个‘c’得到‘acb’字典序更小。

解: 1.

function getAllNums(str, flag) {
    // 判断字符串合法性
    if (!checkRight(str)) return 0
    return getRes(0, str.length - 1, flag)
    function getRes(left, right, type) {
        if (left === right) {
            // 字符串转为数字再转布尔
            return Boolean(+str[left]) === type ? 1 : 0
        }
        let res = 0
        for (let i = left + 1; i < right; i += 2) {
            if (flag) {
                if (str[i] === '&') {
                    res += getRes(left, i - 1, flag) * getRes(i + 1, right, flag)
                }
                if (str[i] === '|') {
                    res += getRes(left, i - 1, flag) * getRes(i + 1, right, flag)
                    res += getRes(left, i - 1, !flag) * getRes(i + 1, right, flag)
                    res += getRes(left, i - 1, flag) * getRes(i + 1, right, !flag)
                }
                if (str[i] === '^') {
                    res += getRes(left, i - 1, flag) * getRes(i + 1, right, !flag)
                    res += getRes(left, i - 1, !flag) * getRes(i + 1, right, flag)
                }
            } else {
                if (str[i] === '&') {
                    res += getRes(left, i - 1, flag) * getRes(i + 1, right, flag)
                    res += getRes(left, i - 1, flag) * getRes(i + 1, right, !flag)
                    res += getRes(left, i - 1, !flag) * getRes(i + 1, right, flag)
                }
                if (str[i] === '|') {
                    res += getRes(left, i - 1, flag) * getRes(i + 1, right, flag)
                }
                if (str[i] === '^') {
                    res += getRes(left, i - 1, flag) * getRes(i + 1, right, flag)
                    res += getRes(left, i - 1, !flag) * getRes(i + 1, right, !flag)
                }
            }
        }
        return res
    }
    function checkRight(str) {
        str = str.split('')
        return str.some((item, idx) => {
            if (idx % 2 === 1) {
                return ['&', '|', '^'].includes(item);
            }
            return ['0', '1'].includes(item);
        })
    }
}
function getMaxLength(str) {
    // 每一个以当前字符为结尾时的子串长度
    const idxMap = new Map()
    const dp = []
    let max = 1
    dp[0] = 1
    idxMap.set(str[0], 0)
    for (let i = 1; i < str.length; i++) {
        const preIdx = idxMap.get(str[i])
        // 当前字符上一次出现的位置
        if (preIdx !== undefined) {
            dp[i] = Math.min(dp[i - 1] + 1, i - preIdx)
        } else {
            dp[i] = dp[i - 1] + 1
        }
        max = Math.max(max, dp[i])
        idxMap.set(str[i], i)
    }
    return max
}
function minDistance(str1, str2) {
    const dp = []
    // dp[i][j]的含义为
    // str1的前i个字符,变为str2的前j个字符的代价
    // 譬如dp[0][3]表明str1中前0个字符,变为str2中前三个字符的代价
    for (let i = 0; i <= str1.length; i++) {
        dp[i] = []
        for (let j = 0; j <= str2.length; j++) {
            if (i === 0) {
                dp[i][j] = j
            }
            if (j === 0) {
                dp[i][j] = i
            }
        }
    }
    for (let i = 1; i <= str1.length;i++) {
        for (let j = 1; j <= str2.length; j++) {
            // 如果最后一个字符相同(因为dp表的i,j代表长度。所以对应str中的位置要-1)
            if (str1[i - 1] === str2[j - 1]) {
                // 那么当前代价就可以忽略掉这个字符
                dp[i][j] = dp[i - 1][j - 1]
            } else {
                // 最后一个字符替换
                const p1 = dp[i - 1][j - 1]
                // str1 把i 位置字符删除
                const p2 = dp[i - 1][j]
                // str1 把 0~i变成 str2的0~j-1。最后增加一个str2的j位置字符
                const p3 = dp[i][j - 1]
                dp[i][j] = Math.min(p1, p2, p3) + 1
            }
        }
    }
    return dp[str1.length][str2.length]
}
function getMinDictionaryOrder(str) {
    let res = ''
    getRes(str)
    return res
    function getRes(curStr) {
        if (!curStr) return
        const hashMap = new Map()
        let tempStr = ''
        let minDicChar = null
        // 建立词频表
        for (let i of curStr) {
            hashMap.set(i, (hashMap.get(i) ?? 0) + 1)
        }
        // 每遇到一个字符就词频减一
        for (let i = 0; i < curStr.length; i++) {
            if (minDicChar === null) {
                minDicChar = i
            } else {
                // 判断字典序,找到到当前位置为止,最小字典序的字符坐标
                minDicChar = curStr[minDicChar].localeCompare(curStr[i]) > 0 ? i : minDicChar
            }
            hashMap.set(curStr[i], hashMap.get(curStr[i]) - 1)
            // 当词频减少到0,说明以后再也不会有这个字符了,说明在0~当前字符时,删掉重复字符可以保证剩余所有字符都有一份及以上的
            if (hashMap.get(curStr[i]) === 0) {
                if (!res.includes(curStr[minDicChar])) res = res + curStr[minDicChar]
                // 删除最小字典序之前的字符
                const reg = new RegExp(`${curStr[minDicChar]}`, 'g')
                tempStr = curStr.substring(minDicChar, i + 1).replace(reg, '') + curStr.substring(i+1)
                break
            }
        }
        getRes(tempStr)
    }
}