算法练习day23

68 阅读2分钟

一、组合总和

该题的递归深度不固定,元素可以重复,所以单层遍历的时候不需要通过startIndex避免重复

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum = function(candidates, target) {
    let result = []
    let path = []
    function backtracking(startIndex = 0, sum = 0) {
        if(sum > target) {
            return
        }
        if(sum === target) {
            result.push([...path])
        }
        for(let i = startIndex; i < candidates.length;i++) {
            path.push(candidates[i])
            backtracking(i, sum + candidates[i])
            path.pop()
        }
    }
    backtracking()
    return result
};

二、组合总和2

递归问题可以看做是一个树,因为集合可能有重复的元素,但是结果中的组合不能重复,所以,遍历的过程中需要重复的元素需要continue跳过

在用一个组合结果中,可以有重复元素,每个元素只能使用一次,所以需要startIndex去重

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum2 = function(candidates, target) {
    let result = []
    let path = []
    function backtracking(startIndex = 0, sum = 0) {
        if(sum === target) {
            result.push([...path])
            return
        }
        for(let i = startIndex; i < candidates.length; i++) {
            if(candidates[i] + sum > target) {
                break
            }
            if(i > startIndex && candidates[i] === candidates[i-1]) {
                continue
            }
            path.push(candidates[i])
            backtracking(i + 1, candidates[i] + sum)
            path.pop()
        }
    }
    candidates.sort((x, y) => x - y)
    backtracking()
    return result
};

三、分割回文串

/**
 * @param {string} s
 * @return {string[][]}
 */
var partition = function(s) {
    if(!s.length) {
        return []
    }
    let result = []
    let path = []
    function backtracking(startIndex = 0) {
        if(startIndex >= s.length) {
            result.push([...path])
            return
        }
        for(let i = startIndex; i < s.length; i++) {
            if(isPalindrome(s, startIndex, i)) {
                path.push(s.substring(startIndex, i+1))
            } else {
                continue
            }
            backtracking(i + 1)
            path.pop()
        }
    }
    backtracking()
    return result
};

function isPalindrome(s, left, right) {
    while(left < right) {
        if(s[left] !== s[right]) {
            return false
        }
        left++
        right--
    }
    return true
}

通过dp记录字符串子串是否为回文字符串,这样优化每次都遍历判断

var partition = function (s) {
    if (!s.length) {
        return []
    }
    let result = []
    let path = []
    let dp = new Array(s.length).fill(0).map(_ => new Array(s.length).fill(false))
    function backtracking(startIndex = 0) {
        if (startIndex >= s.length) {
            result.push([...path])
            return
        }
        for (let i = startIndex; i < s.length; i++) {
            if (dp[startIndex][i]) {
                path.push(s.substring(startIndex, i + 1))
            } else {
                continue
            }
            backtracking(i + 1)
            path.pop()
        }
    }
    function createDp() {
        for (let i = s.length - 1; i >= 0; i--) {
            for (let j = i; j < s.length; j++) {
                if (i === j) {
                    dp[i][j] = true
                } else if (j - i === 1) {
                    dp[i][j] = s[i] === s[j]
                } else {
                    dp[i][j] = s[i] === s[j] && dp[i + 1][j - 1]
                }
            }
        }
        return dp
    }
    createDp()
    backtracking()
    return result
};