四、字符串相关

2023-07-16  本文已影响0人  LucXion
/// 2.单词拆分
/// - Parameters:
///   - s: <#s description#>
///   - wordDict: <#wordDict description#>
/// - Returns: <#description#>
func wordBreak(_ s: String, _ wordDict: [String]) -> Bool {

    if(s.count == 0){
        // 过滤空字符串
        return true
    }
    // 用 26 个字母首字母为桶,装桶
    var buckets:[String:[String]] = [:]
    for word in wordDict {
        if word.count > 0 {
            let first = word.prefix(1)
            if var _ = buckets[String(first)] {
                buckets[String(first)]!.append(word)
            }else {
                buckets.updateValue([word], forKey: String(first))
            }
        }
    }
    
    // 初始化dp数组
    var dp:[Bool] = Array.init(repeating: false, count: s.count)
    // 截取第一个字符
    let first = saAndsa(s: s, index: 0)
    // 第一个字符没匹配上,直接false
    guard let bucket = buckets[first] else { return false }

    // 根据第一个字符的桶,赋值第一批 dp = true
    for word in bucket {
        let length = word.count
        let subStr = String(s.prefix(length))
        if word == subStr {
            dp[length-1] = true
        }
    }
    /*
     动态规划函数:
        如果当前字符串满足条件,那么必定存在中间一个节点 j , 0 - j 被完整截取, j - s.count 是一个单词
     */
    // 第一重遍历,外层遍历,所有可能
    for i in 0...s.count-1 {
        // 当前遍历到 i, iStr = 当前截取到的完整字符串
        let iStr = String(s.prefix(i+1))
        // 内层遍历,找到那个节点 j
        for j in 0...i {
            // 从后往前截取,其实直接截取最后一个单词就可以了?
            let i_j = String(iStr.suffix(i-j))
            // 从后面截取的是否符合单词
            let jFirst = String(i_j.prefix(1))
            if let bucket = buckets[jFirst] {
                // 这里用 && 真的大大节省了效率,dp[j] == false,那么这个j就不会是节点
                if(dp[j] == true && contain(s: i_j, bucket: bucket)){
                    // 如果这段字符串包含满足条件的 j 节点,那么他就是符合条件的
                    dp[i] = true
                }
            }
        }
    }
    return dp[s.count-1]
}


func contain(s:String,bucket:[String])->Bool{
    for word in bucket {
        if (s == word){
            return true
        }
    }
    return false
}

func saAndsa(s:String,index:Int)->String{
    let startIndex = s.index(s.startIndex, offsetBy: index)
    let endIndex = s.index(startIndex, offsetBy: 1)
    let res = String(s[startIndex..<endIndex])
    return res
}



/// 3.最长回文子序列
/// - Parameter s: <#s description#>
/// - Returns: <#description#>
func longestPalindromeSubseq(_ s: String) -> Int {

    var dp:[Combination:Int] = [:]
    let count = s.count
    if count == 0 || count == 1 {
        return count
    }else if(count == 2){
        if saAndsa(s: s, index: 0) == saAndsa(s: s, index: 1){
            return 2
        }else {
            return 1
        }
    }

    // 范围长度 i
    for i in 1...count {
        // 遍历的次数
        for j in 1...count-i+1 {
            let keyi = j-1
            let keyj = (j-1)+i-1
            let key = Combination(i: keyi,j: keyj)
            if (i == 1){
                // 长度为1,绝对是回文
                dp.updateValue(1, forKey: key)
            }else if(i == 2) {
                // 长度为2,比较左右
                if(saAndsa(s: s, index: keyi) == saAndsa(s: s, index: keyj)){
                    dp.updateValue(2, forKey: key)
                }else {
                    dp.updateValue(1, forKey: key)
                }
            }else if(i == 3){
                // 长度为3,比较中间的
                if (saAndsa(s: s, index: keyi) == saAndsa(s: s, index: keyj)){
                    dp.updateValue(3, forKey: key)
                }else {
                    // 比较左右两个dp
                    let combinationLeft = Combination(i: keyi,j: keyi + 1)
                    let combinationRight = Combination(i: keyj - 1,j: keyj)
                    dp.updateValue(max(dp[combinationLeft]!, dp[combinationRight]!), forKey: key)
                }
            }else {
                // 其他长度,如果左右相同,那么中间的加2,如果左右不相同,取左或右的最大值,左右最大值,当中间的两个端不同,那么比较,如果中间两个端相同,不变
                let combinationMid = Combination(i: keyi + 1,j: keyj - 1)
                let combinationLeft = Combination(i:keyi,j: keyj - 1)
                let combinationRight = Combination(i:keyi+1,j: keyj)
                if(saAndsa(s: s, index: keyi) == saAndsa(s: s, index: keyj)){
                    dp.updateValue(dp[combinationMid]! + 2, forKey: key)
                }else {
                    dp.updateValue(max(dp[combinationLeft]!, dp[combinationRight]!), forKey: key)
                }
            }
        }
    }
    return dp[Combination(i: 0,j: count-1)]!
}


/// 4.编辑问题
/// - Parameters:
///   - word1: <#word1 description#>
///   - word2: <#word2 description#>
/// - Returns: <#description#>
func minDistance(_ word1: String, _ word2: String) -> Int {

    // 优化空间复杂度
    var dpi_1:[Combination:Int]=[:]
    var dpi:[Combination:Int]=[:]
    
    // 根据公式建立二维数组
    for i in 0...word1.count {
        dpi.removeAll()
        // 0代表空串,i = word1 字符长度
        for j in 0...word2.count {
            // 0代表空串,j = word1 字符长度
            if i == 0 {
                dpi[Combination(i: 0,j: j)] = j
            }else if(j == 0){
                dpi[Combination(i: i,j: 0)] = i
            }else {
                var lastWordSame = 1
                if(saAndsa(s: word1, index: i-1) == saAndsa(s: word2, index: j-1)){
                    lastWordSame = 0
                }
                /*
                 解题核心:建立dp函数
                 1.dp[i,j]:i对应的word1长度,j对应的word2长度
                 2.dp[i,j]的最短编辑数 = min(dp[i-1,j]+1,dp[i,j-1]+1,dp[i-1,j-1]+word1[last] == word2[last])
                 3.优化空间复杂度(非常重要),dp只需要记录两行,i与i-1
                 */
                dpi[Combination(i: i,j: j)] = min(dpi_1[Combination(i: i-1,j: j)]! + 1, dpi[Combination(i: i,j: j-1)]! + 1,dpi_1[Combination(i: i-1,j: j-1)]! + lastWordSame)
            }
        }
        dpi_1 = dpi
    }
    return dpi[Combination(i: word1.count,j: word2.count)]!
}


/// 5.两个字符串的最小ASCII删除和
/// - Parameters:
///   - s1: <#s1 description#>
///   - s2: <#s2 description#>
/// - Returns: <#description#>
func minimumDeleteSum(_ s1: String, _ s2: String) -> Int {
    
    var dp:[[Int]] = [[Int]](repeating: [Int](repeating: 0, count: s2.count + 1), count: s1.count + 1)
    let s1Arr = s1.map{Int($0.asciiValue!)}
    let s2Arr = s2.map{Int($0.asciiValue!)}
    for m in 1...s1Arr.count {
        // m 代表长度,所以值是累加和
        dp[m][0] = s1Arr.prefix(m).reduce(0, +)
    }
    for n in 1...s2Arr.count {
        dp[0][n] = s2Arr.prefix(n).reduce(0, +)
    }

    for m in 1...s1Arr.count {
        for n in 1...s2Arr.count {
            let valueM = s1Arr[m-1]
            let valueN = s2Arr[n-1]
            if valueM == valueN {
                dp[m][n] = dp[m-1][n-1]
            }else {
                // 当 s1[m] 与 s2[n-3] 相同 ,这种情况已经包含在 dp[m+1][n-2] 中
                dp[m][n] = min(dp[m-1][n] + valueM, dp[m][n-1] + valueN)
            }
        }
    }
    return dp[s1Arr.count][s2Arr.count]
}
上一篇下一篇

猜你喜欢

热点阅读