日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态规划应用--搜索引擎拼写纠错

發(fā)布時間:2024/7/5 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划应用--搜索引擎拼写纠错 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • 1. 字符串相似度
      • 1.1 萊文斯坦距離
      • 1.2 最長公共子串長度
    • 2. 計算編輯距離
      • 2.1 萊文斯坦距離
      • 2.2 最長公共子串長度
    • 3. 搜索引擎拼寫糾錯
    • 4. 練習題

在 Trie樹那節(jié)講過,利用Trie可以進行關鍵詞提示,節(jié)省輸入時間。
在搜索框中你不小心打錯了字,它也會智能提醒你是不是要搜XXX

1. 字符串相似度

  • 如何量化兩個字符串的相似度?有一個非常著名方法,編輯距離(Edit Distance)。
  • 編輯距離,將一個字符串轉化成另一個字符串,需要的最少編輯操作次數(shù)(增加一個字符、刪除一個字符、替換一個字符)。
  • 編輯距離越大,兩個字符串相似度越小;編輯距離越小,兩個字符串相似度越大。兩個完全相同的字符串,編輯距離是0。

1.1 萊文斯坦距離

  • 萊文斯坦距離(Levenshtein distance)允許增加刪除、替換字符這三個編輯操作
  • 萊文斯坦距離的大小,表示兩個字符串差異的大小

1.2 最長公共子串長度

  • 最長公共子串長度(Longest common substring length)只允許增加、刪除字符這兩個編輯操作
  • 表示兩個字符串相似程度的大小

兩個字符串 mitcmu 和 mtacnu 的萊文斯坦距離是3,最長公共子串長度是4。

2. 計算編輯距離

這個問題是求把一個字符串變成另一個字符串,需要的最少編輯次數(shù)。整個求解過程,涉及多個決策階段,需要依次考察一個字符串中的每個字符,跟另一個字符串中的字符是否匹配,所以,問題符合多階段決策最優(yōu)解模型。

2.1 萊文斯坦距離

回溯解法:
回溯是一個遞歸處理的過程。如果a[i]與b[j]匹配,我們遞歸考察a[i+1]和b[j+1]。如果a[i]與b[j]不匹配,那我們有多種處理方式可選:

  • 可以刪除a[i],然后遞歸考察a[i+1]和b[j];
  • 可以刪除b[j],然后遞歸考察a[i]和b[j+1];
  • 可以在a[i]前面添加一個跟b[j]相同的字符,然后遞歸考察a[i]和b[j+1];
  • 可以在b[j]前面添加一個跟a[i]相同的字符,然后遞歸考察a[i+1]和b[j];
  • 可以將a[i]替換成b[j],或者將b[j]替換成a[i],然后遞歸考察a[i+1]和b[j+1]。
/*** @description: 萊文斯坦距離,回溯* @author: michael ming* @date: 2019/7/25 1:25* @modified by: */ #include <string> #include <iostream> using namespace std; void lwstBT(string &a, string &b, int i, int j, int dist, int &minDist) {if(i == a.size() || j == b.size()){if(i < a.size())dist += (a.size()-i);if(j < b.size())dist += (b.size()-j);if(dist < minDist)minDist = dist;return;}if(a[i] == b[j])// 兩個字符匹配{lwstBT(a,b,i+1,j+1,dist,minDist);}else// 兩個字符不匹配{lwstBT(a,b,i+1,j,dist+1,minDist);// 刪除 a[i] 或者 b[j] 前添加一個字符lwstBT(a,b,i,j+1,dist+1,minDist);// 刪除 b[j] 或者 a[i] 前添加一個字符lwstBT(a,b,i+1,j+1,dist+1,minDist);// 將 a[i] 和 b[j] 替換為相同字符} } int main() {int minDist = INT_MAX;string a = "mitcmu", b = "mtacnu";lwstBT(a,b,0,0,0,minDist);cout << "萊文斯坦距離:" << minDist << endl; }


  • 在遞歸樹中,每個節(jié)點狀態(tài)包含三個變量(i,j,dist),dist表示處理到a[i]和b[j]時,已經執(zhí)行的編輯次數(shù)。

  • 在遞歸樹中,(i,j)兩個變量重復的節(jié)點很多,比如(3,2)和(2,3)。對于(i,j)相同的節(jié)點,我們只保留 dist最小的,繼續(xù)遞歸處理,剩下的舍棄。所以,狀態(tài)就從(i,j,dist)變成了(i,j,min_dist),其中min_dist 表示處理到a[i]和b[j],已經執(zhí)行的最少編輯次數(shù)。

  • 這個問題的狀態(tài)轉移方式,要比之前兩節(jié)課中講到的例子要復雜很多。上一節(jié)我們講的矩陣最短路徑問題中,到達狀態(tài)(i,j)只能通過(i-1,j)或(i,j-1)兩個狀態(tài)轉移過來,而今天這個問題,狀態(tài)(i,j)可能從(i-1,j),(i,j-1),(i-1,j-1)三個狀態(tài)中的任意一個轉移過來。

    寫狀態(tài)轉移方程

    如果:a[i] != b[j],那么:min_dist(i, j) 就等于: min{min_dist(i-1,j)+1, min_dist(i,j-1)+1, min_dist(i-1,j-1)+1}如果:a[i] == b[j],那么:min_dist(i, j) 就等于: min{min_dist(i-1,j)+1, min_dist(i,j-1)+1, min_dist(i-1,j-1)}其中,min 表示求三數(shù)中的最小值。

    根據(jù)狀態(tài)轉移方程,填充狀態(tài)表

/*** @description: 萊文斯坦距離,動態(tài)規(guī)劃* @author: michael ming* @date: 2019/7/25 20:40* @modified by: */ #include <string> #include <iostream> using namespace std; int min(int x, int y, int z) {int m = INT_MAX;if(x < m) m = x;if(y < m) m = y;if(z < m) m = z;return m; } int lwstDP(string &a, const int lenA, string &b, const int lenB) {int i, j;int minDist[lenA][lenB];for(j = 0; j < lenB; ++j)//初始化第 0 行:a[0..0] 與 b[0..j] 的編輯距離{if(a[0] == b[j])minDist[0][j] = j;else if(j != 0)minDist[0][j] = minDist[0][j-1]+1;elseminDist[0][j] = 1;}for(i = 0; i < lenA; ++i)//初始化第 0 列:a[0..i] 與 b[0..0] 的編輯距離{if(a[i] == b[0])minDist[i][0] = i;else if(i != 0)minDist[i][0] = minDist[i-1][0]+1;elseminDist[i][0] = 1;}for(i = 1; i < lenA; ++i)//按行填狀態(tài)表for(j = 1; j < lenB; ++j){if(a[i] == b[j])minDist[i][j] = min(minDist[i-1][j]+1, minDist[i][j-1]+1, minDist[i-1][j-1]);elseminDist[i][j] = min(minDist[i-1][j]+1, minDist[i][j-1]+1, minDist[i-1][j-1]+1);}return minDist[lenA-1][lenB-1]; } int main() {string a = "mitcmu", b = "mtacnu";cout << "萊文斯坦距離:" << lwstDP(a,a.size(),b,b.size()) << endl; }

2.2 最長公共子串長度

最長公共子串作為編輯距離的一種,只允許增加刪除字符兩種操作。表征的也是兩個字符串之間的相似程度。

  • 每個狀態(tài)包括三個變量(i,j,max_lcs),max_lcs表示a[0…i]和b[0…j]的最長公共子串長度。那(i,j)這個狀態(tài)都是由哪些狀態(tài)轉移過來的呢?
  • 先來看回溯的處理思路。我們從a[0]和b[0]開始,依次考察兩個字符串中的字符是否匹配。
  • 如果a[i]與b[j]匹配,最大公共子串長度+1,繼續(xù)考察a[i+1]和b[j+1]。
  • 如果a[i]與b[j]不匹配,最長公共子串長度不變,這個時候,有兩個不同的決策路線:
    1 刪除a[i],或者在b[j]前面加上一個字符a[i],然后繼續(xù)考察a[i+1]和b[j];
    2 刪除b[j],或者在a[i]前面加上一個字符b[j],然后繼續(xù)考察a[i]和b[j+1]。

a[0…i]和b[0…j]的最長公共長度max_lcs(i,j),只有可能通過下面三個狀態(tài)轉移過來:
(i-1,j-1,max_lcs),max_Ics表示 a[0…i-1] 和 b[0…j-1] 的最長公共子串長度;
(i-1,j,max_lcs),max_lcs表示 a[0…i-1] 和 b[0…j] 的最長公共子串長度;
(i,j-1,max_lcs),max_Ics表示 a[0…i] 和 b[0…j-1] 的最長公共子串長度。

狀態(tài)方程 如果:a[i] == b[j],那么:max_lcs(i, j) 就等于: max{max_lcs(i-1,j-1)+1, max_lcs(i-1, j), max_lcs(i, j-1)};如果:a[i] != b[j],那么:max_lcs(i, j) 就等于: max{max_lcs(i-1,j-1), max_lcs(i-1, j), max_lcs(i, j-1)};其中 max 表示求三數(shù)中的最大值。 /*** @description: 最長公共子串長度, DP* @author: michael ming* @date: 2019/7/25 21:40* @modified by: */ #include <string> #include <iostream> using namespace std; int max(int x, int y, int z) {int m = INT_MIN;if(x > m) m = x;if(y > m) m = y;if(z > m) m = z;return m; } int lcsDP(string &a, const int lenA, string &b, const int lenB) {int i, j;int maxlcs[lenA][lenB];for(j = 0; j < lenB; ++j)//初始化第 0 行:a[0..0] 與 b[0..j] 的maxlcs{if(a[0] == b[j])maxlcs[0][j] = 1;else if(j != 0)maxlcs[0][j] = maxlcs[0][j-1];elsemaxlcs[0][j] = 0;}for(i = 0; i < lenA; ++i)//初始化第 0 列:a[0..i] 與 b[0..0] 的maxlcs{if(a[i] == b[0])maxlcs[i][0] = 1;else if(i != 0)maxlcs[i][0] = maxlcs[i-1][0];elsemaxlcs[i][0] = 0;}for(i = 1; i < lenA; ++i)//按行填狀態(tài)表for(j = 1; j < lenB; ++j){if(a[i] == b[j])maxlcs[i][j] = max(maxlcs[i-1][j], maxlcs[i][j-1], maxlcs[i-1][j-1]+1);elsemaxlcs[i][j] = max(maxlcs[i-1][j], maxlcs[i][j-1], maxlcs[i-1][j-1]);}return maxlcs[lenA-1][lenB-1]; } int main() {string a = "mitcmu", b = "mtacnu";cout << "最大公共子串長度:" << lcsDP(a,a.size(),b,b.size()) << endl; }

最大公共子串長度:4

3. 搜索引擎拼寫糾錯

  • 當用戶在搜索框內,輸入一個拼寫錯誤的單詞時,拿這個單詞跟詞庫中的單詞一—進行比較,計算編輯距離,將編輯距離最小,作為糾正之后的單詞,提示用戶。

  • 這就是拼寫糾錯最基本的原理。真正商用的搜索引擎,拼寫糾錯功能不會這么簡單。一方面,單純利用編輯距離來糾錯,效果并不一定好;另一方面,詞庫中的數(shù)據(jù)量可能很大,對糾錯的性能要求很高。

  • 針對糾錯效果不好,有很多種優(yōu)化思路。
    a. 取出編輯距離最小的TOP10,然后根據(jù)其他參數(shù)決策選擇。比如使用搜索熱門程度來決定。
    b. 可以用多種編輯距離計算方法,比如今天講到的兩種,分別計算編輯距離最小的TOP10,求交集,用交集的結果,再繼續(xù)優(yōu)化處理。
    c. 可以統(tǒng)計用戶的搜索日志,得到最常拼錯的單詞列表,以及對應的拼寫正確的單詞。糾錯時,首先在這個最常拼錯單詞列表中查找。一旦找到,直接返回對應的正確的單詞。這樣糾錯的效果非常好。
    d. 引入個性化因素。針對每個用戶,維護這個用戶特有的搜索喜好,也就是常用的搜索關鍵詞。當用戶輸入錯誤時,先在這個用戶常用的搜索關鍵詞中,計算編輯距離,查找編輯距離最小的單詞。

  • 針對糾錯性能方面,講兩種分治的優(yōu)化思路。
    a. 如果糾錯功能的TPS(每秒事務處理量(TransactionPerSecond))不高,可以部署多臺機器,每臺機器運行一個獨立的糾錯功能。當有一個糾錯請求的時候,通過負載均衡,分配到其中一臺機器,來計算編輯距離,得到糾錯單詞。
    b. 如果糾錯系統(tǒng)的響應時間太長,也就是,每個糾錯請求處理時間過長,可以將糾錯的詞庫,分割到很多臺機器。當有一個糾錯請求的時候,將這個拼寫錯誤的單詞,同時發(fā)送到多臺機器,并行處理,分別得到編輯距離最小的單詞,然后再比對合并,最終決定出一個最優(yōu)的糾錯單詞。

4. 練習題

LeetCode 72. 編輯距離(DP)

LeetCode 1143. 最長公共子序列(動態(tài)規(guī)劃)

總結

以上是生活随笔為你收集整理的动态规划应用--搜索引擎拼写纠错的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。