高性能なスペルミス修正アルゴリズムを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);
}
}
}
コメント