如何衡量兩個字符串的相似度

設(shè)想一下以下的場景:
1抑片、在游戲中聊天睹栖,我們想要避免玩家不斷地用相同的內(nèi)容或只做微小調(diào)整的內(nèi)容進(jìn)行刷屏硫惕。我們需要判斷玩家每次發(fā)的消息與過去幾次的相似程度。
2野来、在搜索引擎中恼除,當(dāng)用戶輸錯內(nèi)容時,程序能夠進(jìn)行檢測曼氛,將正確的內(nèi)容提示給用戶豁辉。
如何實現(xiàn)這樣的需求呢?

這樣的需求的本質(zhì)舀患,其實是判斷字符串之間的相似程度徽级。在上面的兩個場景中:
1、我們在內(nèi)存中保存玩家最近發(fā)送的10條消息聊浅,當(dāng)玩家發(fā)送新的消息時灰追;拿著新消息與歷史消息進(jìn)行逐一地匹配,判斷它們之間的相似度狗超,當(dāng)相似度達(dá)到一定比例時弹澎,就可以拒絕玩家發(fā)送新的消息。
2努咐、當(dāng)玩家搜索時苦蒿,我們可以使用Trie樹來查詢以玩家輸入為前綴的內(nèi)容;如果查詢不到渗稍,則代表著玩家可能輸入有誤佩迟。這時团滥,可以回退一些字符,獲得匹配的內(nèi)容的列表报强。此時灸姊,再逐一計算列表中的內(nèi)容與用戶輸入內(nèi)容的相似度,將相似度最大的內(nèi)容推薦給用戶秉溉。

而要衡量兩個字符串的相似度力惯,就需要用到編輯距離(Edit Distance)。
編輯距離(Minimum Edit Distance召嘶,MED)父晶,由俄羅斯科學(xué)家 Vladimir Levenshtein 在1965年提出,也因此而得名 Levenshtein Distance弄跌。
在信息論甲喝、語言學(xué)和計算機(jī)科學(xué)領(lǐng)域,Levenshtein Distance 是用來度量兩個序列相似程度的指標(biāo)铛只。通俗地來講埠胖,編輯距離指的是在兩個單詞之間,由其中一個單詞轉(zhuǎn)換為另一個單詞所需要的最少單字符編輯操作次數(shù)淳玩。

在這里定義的單字符編輯操作有且僅有三種:

  • 插入(Insertion)
  • 刪除(Deletion)
  • 替換(Substitution)

譬如直撤,"kitten" 和 "sitting" 這兩個單詞,由 "kitten" 轉(zhuǎn)換為 "sitting" 需要的最少單字符編輯操作有:
1.kitten → sitten (substitution of "s" for "k")
2.sitten → sittin (substitution of "i" for "e")
3.sittin → sitting (insertion of "g" at the end)

因此凯肋,"kitten" 和 "sitting" 這兩個單詞之間的編輯距離為 3 。

而實現(xiàn)編輯距離的最經(jīng)典的方式就是使用動態(tài)規(guī)劃(Dynamic Programming)汽馋。代碼如下:

/*
    使用編輯距離Edit Distance的方式來計算兩個字符串的相似類侮东。
    參考資料:https://en.wikipedia.org/wiki/Edit_distance
*/
package stringUtil

// 計算兩個字符串的相似度
// word1: 第一個字符串
// word2: 第二個字符串
// 返回值:
// 兩個字符串的距離,表示兩個字符串經(jīng)過多少次變換豹芯,可以變成同一個字符串
// 兩個字符串的相似度悄雅,范圍為[0, 1]
func Similarity(word1, word2 string) (distance int, similarity float64) {
    // 內(nèi)部方法,用于計算最小值铁蹈、最大值
    min := func(nums ...int) int {
        _min := nums[0]
        for _, v := range nums {
            if v < _min {
                _min = v
            }
        }
        return _min
    }
    max := func(nums ...int) int {
        _max := nums[0]
        for _, v := range nums {
            if v > _max {
                _max = v
            }
        }
        return _max
    }

    // 如果有單詞為空宽闲,或者單詞相同,則直接計算出結(jié)果握牧,無需進(jìn)一步計算
    m, n := len(word1), len(word2)
    if m == 0 {
        distance = n
        return
    }
    if n == 0 {
        distance = m
        return
    }
    if m == n && word1 == word2 {
        distance = 0
        similarity = 1
        return
    }

    // 使用動態(tài)規(guī)劃計算編輯距離(Edit Distance)
    // Step 1: define the data structure
    dp := make([][]int, m+1, m+1)
    for i := 0; i <= m; i++ {
        dp[i] = make([]int, n+1, n+1)
    }

    // Step 2: init the data
    for i := 0; i <= m; i++ {
        for j := 0; j <= n; j++ {
            if i == 0 {
                dp[i][j] = j
            }
            if j == 0 {
                dp[i][j] = i
            }
        }
    }

    // Step 3: state transition equation
    for i := 1; i <= m; i++ {
        for j := 1; j <= n; j++ {
            if word1[i-1] == word2[j-1] {
                dp[i][j] = dp[i-1][j-1]
            } else {
                dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
            }
        }
    }

    // 得到距離并計算相似度
    distance = dp[m][n]
    maxLength := max(m, n)
    similarity = float64(maxLength-distance) / float64(maxLength)

    return
}

完整的代碼容诬,請參考:https://github.com/Jordanzuo/goutil/blob/master/stringUtil/similarity.go

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沿腰,隨后出現(xiàn)的幾起案子览徒,更是在濱河造成了極大的恐慌,老刑警劉巖颂龙,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件习蓬,死亡現(xiàn)場離奇詭異纽什,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)躲叼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門芦缰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人枫慷,你說我怎么就攤上這事让蕾。” “怎么了流礁?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵涕俗,是天一觀的道長。 經(jīng)常有香客問我神帅,道長再姑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任找御,我火速辦了婚禮元镀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霎桅。我一直安慰自己栖疑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布滔驶。 她就那樣靜靜地躺著遇革,像睡著了一般。 火紅的嫁衣襯著肌膚如雪揭糕。 梳的紋絲不亂的頭發(fā)上萝快,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音著角,去河邊找鬼揪漩。 笑死,一個胖子當(dāng)著我的面吹牛吏口,可吹牛的內(nèi)容都是我干的奄容。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼产徊,長吁一口氣:“原來是場噩夢啊……” “哼昂勒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起舟铜,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤叁怪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后深滚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奕谭,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涣觉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了血柳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片官册。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖难捌,靈堂內(nèi)的尸體忽然破棺而出膝宁,到底是詐尸還是另有隱情,我是刑警寧澤根吁,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布员淫,位于F島的核電站,受9級特大地震影響击敌,放射性物質(zhì)發(fā)生泄漏介返。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一沃斤、第九天 我趴在偏房一處隱蔽的房頂上張望圣蝎。 院中可真熱鬧,春花似錦衡瓶、人聲如沸徘公。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽关面。三九已至,卻和暖如春十厢,著一層夾襖步出監(jiān)牢的瞬間等太,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工寿烟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澈驼,地道東北人辛燥。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓筛武,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挎塌。 傳聞我的和親對象是個殘疾皇子徘六,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360