算法记录 - 字母大小写全排列

3 阅读2分钟

题目

784.字母大小写全排列

给定一个字符串 s ,通过将字符串 s 中的每个字母转变大小写,我们可以获得一个新的字符串。

返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。

思路

分析题目,例如a1b2, a可以选择a或A,每一步都有多种选择,这是决策树问题,需要所有可能

那就应该想到DFS(回溯)

看到全排列,所有组合,所有可能,所有路径,都可以考虑DFS

实现

  • 定义一个结果数组 放所有组合,一个临时路径 放正在构建的字符串。
  • 定义一个递归函数,并直接从下标0执行。
  • 函数内部判定临时路径长度等于字符串长度就可以加入结果数组并返回了。
  • 之后取当前遍历的字符,判定是数字就加入临时路径,下标加一,递归处理下一个字符,处理完再弹出来,恢复现场。
  • 字母也是同理,区分大小写分别处理。
  • 最后返回结果数组

题解

var letterCasePermutation = function(s) {
    const res = []
    const path = []
    function dfs(index) {
        if(path.length === s.length) {
            res.push(path.join(''))
            return
        }
        const ch = s[index]
        if(!isNaN(ch)) {
            path.push(ch)
            dfs(index + 1)
            path.pop()
        } else {
            path.push(ch.toLowerCase())
            dfs(index + 1)
            path.pop()
            path.push(ch.toUpperCase())
            dfs(index + 1)
            path.pop()
        }
    }
    dfs(0)
    return res
};

复杂度

时间;n*2^L L是字母个数

空间:n*2^L 同上

优化

分析题目发现 [A, a], [A1, a1] , [A1b, A1B, a1b, a1 B],状态是一层一层增长的

生成一批新的字符串,处理下一个字符的时候,把这批字符串扩展成新的一批

可以用状态扩展/BFS,虽然时间空间复杂度一致,但实际运行应该会快些,没有函数调用开销,也没有栈溢出风险。

function foo(s) {
    let res = ['']
    for(let a of s) {
        let cur = []
        if(!isNaN(a)) {
            for(let pre of res) {
                cur.push(pre + a)
            }
        } else {
            for(let pre of res) {
                cur.push(pre + a.toLowerCase())
                cur.push(pre + a.toUpperCase())
            }
        }
        res = cur;
    }
    return res
}

DFS: 按深度遍历,一条走到底。 BFS:按层遍历,每次扩展中间状态