成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

實(shí)現(xiàn)拼寫(xiě)檢查器(spell check)

Harriet666 / 1855人閱讀

摘要:本文同時(shí)發(fā)在我的博客上,歡迎在百度或者搜索的時(shí)候,有時(shí)會(huì)小手一抖,打錯(cuò)了個(gè)別字母,比如我們想搜索,錯(cuò)打成了,但神奇的是,即使我們敲下回車(chē),搜索引擎也會(huì)自動(dòng)搜索而不是,這是怎么實(shí)現(xiàn)的呢本文就將從頭實(shí)現(xiàn)一個(gè)版的拼寫(xiě)檢查器基礎(chǔ)理論首先,我們要確定

本文同時(shí)發(fā)在我的github博客上,歡迎star

在百度或者Google搜索的時(shí)候,有時(shí)會(huì)小手一抖,打錯(cuò)了個(gè)別字母,比如我們想搜索apple,錯(cuò)打成了appel,但神奇的是,即使我們敲下回車(chē),搜索引擎也會(huì)自動(dòng)搜索apple而不是appel,這是怎么實(shí)現(xiàn)的呢?本文就將從頭實(shí)現(xiàn)一個(gè)JavaScript版的拼寫(xiě)檢查器

基礎(chǔ)理論

首先,我們要確定如何量化敲錯(cuò)單詞的概率,我們將原本想打出的單詞設(shè)為origin(O),錯(cuò)打的單詞設(shè)為error(E)

貝葉斯定理我們可知:P(O|E)=P(O)*P(E|O)/P(E)

P(O|E)是我們需要的結(jié)果,也就是在打出錯(cuò)誤單詞E的情況下,原本想打的單詞是O的概率

P(O)我們可以看作是O出現(xiàn)的概率,是先驗(yàn)概率,這個(gè)我們可以從大量的語(yǔ)料環(huán)境中獲取

P(E|O)是原本想打單詞O卻打成了E的概率,這個(gè)可以用最短編輯距離模擬概率,比如原本想打的單詞是apple,打成applee(最短編輯距離為1)的概率比appleee(最短編輯距離為2)自然要大

P(E)由于我們已知E,這個(gè)概念是固定的,而我們需要對(duì)比的是P(O1|E)、P(O2|E)...P(On|E)的概率,不需要精確的計(jì)算值,我們可以不用管它

具體實(shí)現(xiàn)

這部分的實(shí)現(xiàn)我參考了natural的代碼,傳送門(mén)

首先是構(gòu)造函數(shù):

function SpellCheck(priorList) {
    //to do trie
    this.priorList = priorList;
    this.priorHash = {};
    priorList.forEach(item => {
        !this.priorHash[item] && (this.priorHash[item] = 0);
        this.priorHash[item]++;
    });
}

priorList是語(yǔ)料庫(kù),在構(gòu)造函數(shù)中我們對(duì)priorList中的單詞進(jìn)行了出現(xiàn)次數(shù)的統(tǒng)計(jì),這也就可以被我們看作是先驗(yàn)概率P(O)

接下來(lái)是check函數(shù),用來(lái)檢測(cè)這個(gè)單詞是否在語(yǔ)料庫(kù)中出現(xiàn)

SpellCheck.prototype.check = function(word) {
    return this.priorList.indexOf(word) !== -1;
};

然后我們需要獲取單詞指定編輯距離內(nèi)的所有可能性:

SpellCheck.prototype.getWordsByMaxDistance = function(wordList, maxDistance) {
    if (maxDistance === 0) {
        return wordList;
    }
    const listLength = wordList.length;
    wordList[listLength] = [];
    wordList[listLength - 1].forEach(item => {
        wordList[listLength].push(...this.getWordsByOneDistance(item));
    });
    return this.getWordsByMaxDistance(wordList, maxDistance - 1);
};
SpellCheck.prototype.getWordsByOneDistance = function(word) {
    const alphabet = "abcdefghijklmnopqrstuvwxyz";
    let result = [];
    for (let i = 0; i < word.length + 1; i++) {
        for (let j = 0; j < alphabet.length; j++) {
            //插入
            result.push(
                word.slice(0, i) + alphabet[j] + word.slice(i, word.length)
            );
            //替換
            if (i > 0) {
                result.push(
                    word.slice(0, i - 1) +
                        alphabet[j] +
                        word.slice(i, word.length)
                );
            }
        }
        if (i > 0) {
            //刪除
            result.push(word.slice(0, i - 1) + word.slice(i, word.length));
            //前后替換
            if (i < word.length) {
                result.push(
                    word.slice(0, i - 1) +
                        word[i] +
                        word[i - 1] +
                        word.slice(i + 1, word.length)
                );
            }
        }
    }
    return result.filter((item, index) => {
        return index === result.indexOf(item);
    });
};

wordList是一個(gè)數(shù)組,它的第一項(xiàng)是只有原始單詞的數(shù)組,第二項(xiàng)是存放距離原始單詞編輯距離為1的單詞數(shù)組,以此類(lèi)推,直到到達(dá)了指定的最大編輯距離maxDistance

以下四種情況被視為編輯距離為1:

插入一項(xiàng),比如ab->abc

替換一項(xiàng),比如ab->ac

刪除一項(xiàng),比如ab->a

前后替換,比如ab->ba

獲取了所有在指定編輯距離的單詞候選集,再比較它們的先驗(yàn)概率:

SpellCheck.prototype.getCorrections = function(word, maxDistance = 1) {
    const candidate = this.getWordsByMaxDistance([[word]], maxDistance);
    let result = [];
    candidate
        .map(candidateList => {
            return candidateList
                .filter(item => this.check(item))
                .map(item => {
                    return [item, this.priorHash[item]];
                })
                .sort((item1, item2) => item2[1] - item1[1])
                .map(item => item[0]);
        })
        .forEach(item => {
            result.push(...item);
        });
    return result.filter((item, index) => {
        return index === result.indexOf(item);
    });
};

最后得到的就是修正后的單詞

我們來(lái)測(cè)試一下:

const spellCheck = new SpellCheck([
    "apple",
    "apples",
    "pear",
    "grape",
    "banana"
]);
spellCheck.getCorrectionsByCalcDistance("appel", 1); //[ "apple" ]
spellCheck.getCorrectionsByCalcDistance("appel", 2); //[ "apple", "apples" ]

可以看到,在第一次測(cè)試的時(shí)候,我們指定了最大編輯距離為1,輸入了錯(cuò)誤的單詞appel,最后返回修正項(xiàng)apple;而在第二次測(cè)試時(shí),將最大編輯距離設(shè)為2,則返回了兩個(gè)修正項(xiàng)

語(yǔ)料庫(kù)較少的情況

上面的實(shí)現(xiàn)方法是先獲取了單詞所有指定編輯距離內(nèi)的候選項(xiàng),而在語(yǔ)料庫(kù)單詞較少的情況下,這種方法比較耗費(fèi)時(shí)間,我們可以改成先獲取語(yǔ)料庫(kù)中符合指定最短編輯距離的單詞

計(jì)算最短編輯距離是一種比較經(jīng)典的動(dòng)態(tài)規(guī)劃(leetcode:72),dp即可。這里的計(jì)算最短編輯距離與leetcode的情況略有不同,需要多考慮一層臨近字母左右替換的情況

leetcode情況下的狀態(tài)轉(zhuǎn)換方程:

dp[i][j]=0 i===0,j===0

dp[i][j]=j i===0,j>0

dp[i][j]=i j===0,i>0

min(dp[i-1][j-1]+cost,dp[i-1][j]+1,dp[i][j-1]+1) i,j>0

其中當(dāng)word1[i-1]===word2[j-1]時(shí),cost為0,否則為1

考慮臨近字母左右替換的情況,則需要在i>1,j>1且word1[i - 2] === word2[j - 1]&&word1[i - 1] === word2[j - 2]為true的條件下,再作min(dp[i-1][j-1]+cost,dp[i-1][j]+1,dp[i][j-1]+1,dp[i-2][j-2]+1)

拿到語(yǔ)料庫(kù)中符合指定最短編輯距離的單詞在對(duì)先驗(yàn)概率作比較,代碼如下:

SpellCheck.prototype.getCorrectionsByCalcDistance = function(
    word,
    maxDistance = 1
) {
    const candidate = [];
    for (let key in this.priorHash) {
        this.calcDistance(key, word) <= maxDistance && candidate.push(key);
    }
    return candidate
        .map(item => {
            return [item, this.priorHash[item]];
        })
        .sort((item1, item2) => item2[1] - item1[1])
        .map(item => item[0]);
};
SpellCheck.prototype.calcDistance = function(word1, word2) {
    const length1 = word1.length;
    const length2 = word2.length;
    let dp = [];
    for (let i = 0; i <= length1; i++) {
        dp[i] = [];
        for (let j = 0; j <= length2; j++) {
            if (i === 0) {
                dp[i][j] = j;
                continue;
            }
            if (j === 0) {
                dp[i][j] = i;
                continue;
            }
            const replaceCost =
                dp[i - 1][j - 1] + (word1[i - 1] === word2[j - 1] ? 0 : 1);
            let transposeCost = Infinity;
            if (
                i > 1 &&
                j > 1 &&
                word1[i - 2] === word2[j - 1] &&
                word1[i - 1] === word2[j - 2]
            ) {
                transposeCost = dp[i - 2][i - 2] + 1;
            }
            dp[i][j] = Math.min(
                replaceCost,
                transposeCost,
                dp[i - 1][j] + 1,
                dp[i][j - 1] + 1
            );
        }
    }
    return dp[length1][length2];
};
最后

這份代碼還有很多可以優(yōu)化的地方,比如check函數(shù)使用的是indexOf判斷單詞是否在語(yǔ)料庫(kù)中出現(xiàn),我們可以改用單詞查找樹(shù)(Trie)或者h(yuǎn)ash的方式加速查詢

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102271.html

相關(guān)文章

  • java 英文單詞拼寫(xiě)糾正框架(Word Checker)

    Word Checker word checker 本項(xiàng)目用于單詞拼寫(xiě)檢查。 Github 地址 項(xiàng)目簡(jiǎn)介 本項(xiàng)目用于單詞拼寫(xiě)檢查。 特性說(shuō)明 支持 i18n 錯(cuò)誤提示支持 i18N 支持英文的單詞糾錯(cuò) 可以迅速判斷當(dāng)前單詞是否拼寫(xiě)錯(cuò)誤 可以返回最佳匹配結(jié)果 可以返回糾正匹配列表,支持指定返回列表的大小 后續(xù)將會(huì)添加的新功能 英文單詞支持自行定義 中文單詞的拼寫(xiě)是否正確功能添加 快速開(kāi)始 ...

    amc 評(píng)論0 收藏0
  • JavaScript是如何工作的:Web Workers的構(gòu)建塊+ 5個(gè)使用他們的場(chǎng)景

    摘要:最后,提供個(gè)正確使用的場(chǎng)景。異步編程的一個(gè)很好的用例就請(qǐng)求。這意味著異步函數(shù)只能解決一小部分語(yǔ)言單線程中的局限性問(wèn)題。中有類(lèi)似的集群子進(jìn)程概念,他們也是多線程但是和還是有區(qū)別??捎玫奶匦杂捎诘亩嗑€程特性,工作者只能訪問(wèn)特性的一個(gè)子集。 showImg(https://segmentfault.com/img/bVblS8J?w=400&h=298); 這是專(zhuān)門(mén)探索 JavaScript...

    ningwang 評(píng)論0 收藏0
  • sublime text3配置<python篇>

    摘要:選中一個(gè)后,按此快捷鍵可以同時(shí)選中另一個(gè),同時(shí)多了另一個(gè)光標(biāo)在下面新開(kāi)一行在上面新開(kāi)一行刪除整行。向左單位性地移動(dòng)光標(biāo),快速移動(dòng)光標(biāo)。開(kāi)啟關(guān)閉側(cè)邊欄。插件能為提供括號(hào),引號(hào)這類(lèi)高亮功能。用來(lái)安裝其官網(wǎng)上的所有主題。 古語(yǔ)有云,工欲善其事必先利其器。選擇一個(gè)好的工具,往往事半功倍。因?yàn)閭€(gè)人電腦原因,用 pycharm 太卡,所以想起了 sublime text,配置了一下,覺(jué)得挺好用。 ...

    陳江龍 評(píng)論0 收藏0
  • vscode常用插件【全了】

    摘要:插件集待補(bǔ)充。。。同時(shí),它還包含了用于轉(zhuǎn)換為格式和生成數(shù)據(jù)模式的選項(xiàng)用于壓縮合并和文件的應(yīng)用程序。它提供了大量自定義的設(shè)置,以及自動(dòng)壓縮保存并導(dǎo)出為文件的選項(xiàng)。修改文本的更多命名格式,包括駝峰命名下劃線分隔命名,命名以及命名等切換漂亮的主題 插件集 待補(bǔ)充。。。 20180903 文件 【Path Intellisense】 自動(dòng)補(bǔ)全路徑 瀏覽器 【Open-In-Browser】在...

    kyanag 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<