高性能なスペルミス修正アルゴリズムをHow to Write a Spelling Correctorで見つけたので紹介します。
リンク先では、理論的背景とコードも説明されていますので、参考になるかと思います。
ここのJavaバージョンのコードを私なりに書き直してみました。
package com.dukesoftware.spellcorrector;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SpellCorrector {
    public static void main(String args[]) throws IOException {
        Map<String, Integer> nWords = new HashMap<String, Integer>()
        {{
            put("spell", 1);
        }};
        SpellCorrector spellCorrector = new SpellCorrector(nWords);
        System.out.println(spellCorrector.correct("superl"));
    }
    private final Map<String, Integer> nWords;
    private final String[] a_to_z;
    public SpellCorrector(Map<String, Integer> nWords) throws IOException {
        this.nWords = nWords;
        this.a_to_z = createAtoZStringArray();
    }
    private String[] createAtoZStringArray() {
        String[] a_to_z = new String['z' - 'a' + 1];
        for(char c = 'a'; c <= 'z'; ++c)
        {
            a_to_z[c-'a'] = String.valueOf(c);
        }
        return a_to_z;
    }
    private List<String> edits(String word) {
        List<String> result = new ArrayList<>();
        final int len = word.length();
        final int len_m1 = len - 1;
        final String[] a_to_z = this.a_to_z;
        for (int i = 0; i < len_m1; ++i) {
            final String top = word.substring(0, i);
            final String middle = word.substring(i);
            final String bottom = word.substring(i + 1);
            result.add(top + bottom);
            for (String c : a_to_z) {
                result.add(top + c + bottom);
                result.add(top + c + middle);
            }
            result.add(top + word.substring(i + 1, i + 2) + word.substring(i, i + 1) + word.substring(i + 2));
        }
        final String word_0_len    = word.substring(0, len);
        final String word_0_len_m1 = word.substring(0, len_m1);
        final String word_len      = word.substring(len);
        final String word_len_m1   = word.substring(len_m1);
        result.add(word_0_len_m1 + word_len);
        for (String c : a_to_z) {
           result.add(word_0_len_m1 + c + word_len);
           result.add(word_0_len_m1 + c + word_len_m1);
           result.add(word_0_len + c + word_len);
        }
        
        return result;
    }
    public final String correct(String word) {
        if (nWords.containsKey(word)) {
            return word;
        }
        List<String> list = edits(word);
        Map<Integer, String> candidates = new HashMap<>();
        for (String s : list) {
            putToCandidates(s, candidates);
        }
        if (candidates.size() > 0) {
            return candidates.get(Collections.max(candidates.keySet()));
        }
        for (String s : list) {
            for (String w : edits(s)) {
                putToCandidates(w, candidates);
            }
        }
        return candidates.size() > 0 ? candidates.get(Collections.max(candidates.keySet())) : word;
    }
    private void putToCandidates(String w, Map<Integer, String> candidates) {
        if (nWords.containsKey(w)) {
            candidates.put(nWords.get(w), w);
        }
    }
}
コメント