【问题标题】:Levenshtein distance in Swift3Swift3 中的 Levenshtein 距离
【发布时间】:2017-05-21 21:58:53
【问题描述】:

我正在使用来自Rosetta Code 的教程来计算 Levenshtein 距离。他们的代码似乎在 Swift2 中,所以我在执行此操作时收到此错误 Binary operator '+' cannot be applied to operands of type '[Int]' and 'Repeated<String.CharacterView>'var cur = [i + 2] + empty where let empty = repeatElement(s, count: 0)。我该怎么办?

【问题讨论】:

  • 不是对您问题的直接回答,但这里 *.com/questions/26990394/… 是一个实现,只需要对 Swift 3 进行少量修改。
  • 谢谢,工作了,但仍然喜欢使用他们的(Rosetta)方法,因为它看起来更短。

标签: swift swift3 levenshtein-distance rosetta-code


【解决方案1】:

需要进行一些更改。

  • Array 的构造为空。
  • enumerate() 现在是 enumerated()
  • successor() 不再存在,所以我将其替换为 +1

所以现在的功能是

斯威夫特 4:

func levDis(_ w1: String, _ w2: String) -> Int {
    let empty = [Int](repeating:0, count: w2.count)
    var last = [Int](0...w2.count)

    for (i, char1) in w1.enumerated() {
        var cur = [i + 1] + empty
        for (j, char2) in w2.enumerated() {
            cur[j + 1] = char1 == char2 ? last[j] : min(last[j], last[j + 1], cur[j]) + 1
        }
        last = cur
    }
    return last.last!
}

斯威夫特 3:

func levDis(w1: String, w2: String) -> Int {

    let (t, s) = (w1.characters, w2.characters)

    let empty = Array<Int>(repeating:0, count: s.count)
    var last = [Int](0...s.count)

    for (i, tLett) in t.enumerated() {
        var cur = [i + 1] + empty
        for (j, sLett) in s.enumerated() {
            cur[j + 1] = tLett == sLett ? last[j] : min(last[j], last[j + 1], cur[j])+1
        }
        last = cur
    }
    return last.last!
}

【讨论】:

    【解决方案2】:

    基于@Spads 答案更新和改进了对 Swift 4 的答案。

    extension String {
        func levenshteinDistanceScore(to string: String, ignoreCase: Bool = true, trimWhiteSpacesAndNewLines: Bool = true) -> Float {
    
            var firstString = self
            var secondString = string   
    
            if ignoreCase {
                firstString = firstString.lowercased()
                secondString = secondString.lowercased()
            }
            if trimWhiteSpacesAndNewLines {
                firstString = firstString.trimmingCharacters(in: .whitespacesAndNewlines)
                secondString = secondString.trimmingCharacters(in: .whitespacesAndNewlines)
            }
    
            let empty = [Int](repeating:0, count: secondString.count)
            var last = [Int](0...secondString.count)
    
            for (i, tLett) in firstString.enumerated() {
                var cur = [i + 1] + empty
                for (j, sLett) in secondString.enumerated() {
                    cur[j + 1] = tLett == sLett ? last[j] : Swift.min(last[j], last[j + 1], cur[j])+1
                }
                last = cur
            }
    
            // maximum string length between the two
            let lowestScore = max(firstString.count, secondString.count)
    
            if let validDistance = last.last {
                return  1 - (Float(validDistance) / Float(lowestScore))
            }
    
            return 0.0
        }
    }
    
    infix operator =~
    func =~(string: String, otherString: String) -> Bool {
        return string.levenshteinDistanceScore(to: otherString) >= 0.85
    }
    

    【讨论】:

    • 这实际计算的是什么?据我所知,Levenshtein Distance 是 Int,而不是 float。
    • 这对我来说也没有意义。
    • IDK 如果这仍然有效,但基本上这应该返回一个介于 0 a 和 1 之间的值,0 表示字符串完全不同,1 表示字符串相同;它返回两个字符串的相似程度。
    【解决方案3】:
    func ~=(string: String, otherString: String) -> Bool {
        return string.levenshteinDistanceScore(to: otherString) >= 0.85 
    }
    

    【讨论】:

    【解决方案4】:

    由于@Daniel Illescas 的回答不起作用,这里是带有Int 返回类型和断言的工作版本。

    extension String {
    
        func levenshteinDistance(to string: String, ignoreCase: Bool = true, trimWhiteSpacesAndNewLines: Bool = true) -> Int {
            
            var firstString = self
            var secondString = string
            
            if ignoreCase {
                firstString = firstString.lowercased()
                secondString = secondString.lowercased()
            }
            
            if trimWhiteSpacesAndNewLines {
                firstString = firstString.trimmingCharacters(in: .whitespacesAndNewlines)
                secondString = secondString.trimmingCharacters(in: .whitespacesAndNewlines)
            }
            
            let empty = [Int](repeating: 0, count: secondString.count)
            var last = [Int](0...secondString.count)
            
            for (i, tLett) in firstString.enumerated() {
                var cur = [i + 1] + empty
                for (j, sLett) in secondString.enumerated() {
                    cur[j + 1] = tLett == sLett ? last[j] : Swift.min(last[j], last[j + 1], cur[j]) + 1
                }
                
                last = cur
            }
            
            if let validDistance = last.last {
                return validDistance
            }
            
            assertionFailure()
            return 0
        }
    }
    

    【讨论】: