编辑距离(Minimum Edit Distance)

编辑距离(Minimum Edit Distance,MED),也叫 Levenshtein Distance。他的含义是计算字符串a转换为字符串b的最少单字符编辑次数。编辑操作有:插入、删除、替换(都是对a进行的变换)。用lev(i, j) 表示 a的前i个单词和 b的前j个单词的最短编辑距离(即从后往前)。可以分为以下几种情况:

  • i == 0j == 0
    • \(lev(i, j) = max(i, j)\)
  • i,j 不为0, 且 \(a[i] == a[j]\)
    • \(lev(i, j) = lev(i-1, j-1)\)
  • i,j 不为0, 且 \(a[i] != a[j]\)
    • 插入: \(lev(i, j-1)\)
    • 删除: \(lev(i-1, j)\)
    • 替换: \(lev(i-1, j-1) + 1\)
    • 三者取最小

这里的插入是在 a[i] 后方插入,这样,b[j] 已经和 a[i] 后方的元素匹配,所以j前移;删除表示删除当前元素,a[i] 前面的元素顶上来,所以i前移,但是顶上来的元素不一定和j匹配,所以j不动。

C++ 实现

解法就是动态规划:

class Solution {
public:
	vector<vector<int>> matrix;
	int editDistance(string str1, string str2) {
		int len1 = str1.size(); 
		int len2 = str2.size();

		matrix = vector<vector<int>>(len1 + 1, vector<int>(len2 + 1, 0));

		for (int i = 0; i <= len1; ++i)
			matrix[i][0] = i;

		for (int j = 1; j <= len2; ++j)
			matrix[0][j] = j;

		for (int i = 1; i <= len1; ++i) {
			for (int j = 1; j <= len2; ++j) {
				if (str1[i] == str2[j])		matrix[i][j] = matrix[i - 1][j - 1];	// 最后一个字母相同
				else {
					int insert = matrix[i][j - 1] + 1;		// 插入
					int del = matrix[i - 1][j] + 1;			// 删除
					int replace = matrix[i - 1][j - 1] + 1;	// 替换
					matrix[i][j] = min(insert, min(del, replace));
				}
			}
		}
		return matrix[len1][len2];
	}
};

python-Levenshtein 库

pip install python-Levenshtein

import Levenshtein
print(Levenshtein.distance("the","teh"))

单词纠错

前几天看到一个很有意思的代码,通过统计的方式,计算最后可能的单词。w是输入单词,c是可能的正确单词。根据贝叶斯,按照惯例忽略分母。

\[p(c | w)=\frac{p(c) * p(w | c)}{p(w)} \]

  • \(p(w|c)\) : 计算通过w所有编辑距离为i的所有正确单词(就是c),i越小表示p越大
  • \(p(c)\) : 在上面的c中,找出频率最高的单词
import re
from collections import Counter

def words(text): return re.findall(r'\w+', text.lower())

WORDS = Counter(words(open('big.txt').read()))

def P(word, N=sum(WORDS.values())): 
    # 返回单词的概率
    return WORDS[word] / N

def correction(word): 
    # 找到频率最高的c
    return max(candidates(word), key=P)

def candidates(word): 
    '''
    找到候选单词c
    '''
    return (known([word]) or known(edits1(word)) or known(edits2(word)) or [word])

def known(words): 
    # 返回 words 和 WORDS的交集. 找出正确单词
    return set(w for w in words if w in WORDS)

def edits1(word):
    # 编辑距离为1的所有单词
    "All edits that are one edit away from `word`."
    letters    = 'abcdefghijklmnopqrstuvwxyz'
    splits     = [(word[:i], word[i:])    for i in range(len(word) + 1)]
    deletes    = [L + R[1:]               for L, R in splits if R]
    transposes = [L + R[1] + R[0] + R[2:] for L, R in splits if len(R)>1]
    replaces   = [L + c + R[1:]           for L, R in splits if R for c in letters]
    inserts    = [L + c + R               for L, R in splits for c in letters]
    return set(deletes + transposes + replaces + inserts)

def edits2(word): 
    # 编辑距离为2的所有单词
    return (e2 for e1 in edits1(word) for e2 in edits1(e1))

在python中 list1 or list2 的含义是:

  • 如果list1不为空,那么返回list1
  • 如果list1为空,那么返回list2

所以这行代码的意思是:

return (known([word]) or known(edits1(word)) or known(edits2(word)) or [word])
  • 如果单词是正确的就直接返回
  • 如果但是错的,就返回编辑距离是1的所有正确单词作为候选词
  • 如果编辑距离是1的正确单词没有,就返回编辑距离是2的所有正确单词作为候选词
  • 如果还是为空,就返回他自己
posted @ 2020-07-27 11:24  twilight0402  阅读(5)  评论(0编辑  收藏