レーベンシュタイン距離を実装するには,Wikipediaを見れば十分です.
ここのサンプルに忠実に,例えばJavaで実装してみると,次のようになります.
以下,日本語コードはUTF-8だけに統一します.
import java.io.*; import java.util.*; class LevenshteinDistance { int edit(String px, String py) throws UnsupportedEncodingException { int len1=px.length(),len2=py.length(); int[][] row = new int[len1+1][len2+1]; int i,j; int result; for(i=0;i<len1+1;i++) row[i][0] = i; for(i=0;i<len2+1;i++) row[0][i] = i; for(i=1;i<=len1;++i) { for(j=1;j<=len2;++j) { row[i][j] = Math.min(Math.min( (Integer)(row[i-1][j-1]) + ((px.substring(i-1,i).equals(py.substring(j-1,j)))?0:1) , // replace (Integer)(row[i][j-1]) + 1), // delete (Integer)(row[i-1][j]) + 1); // insert } } result=(Integer)(row[len1][len2]); return result; } public static void main(String[] args) throws UnsupportedEncodingException { LevenshteinDistance ld = new LevenshteinDistance() ; System.out.println(ld.edit("あい","あいう")); } }
このプログラムをedit0.javaというファイル名で保存して実行すると...
> javac -encoding UTF-8 edit0.java ; java -Dfile.encoding=UTF8 LevenshteinDistance 1
要するに「う」だけ違うので,距離は1となります.
同じプログラムをC++で作ると...
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class LevenshteinDistance
{
public:
static int edit(const string &px, const string &py)
{
int len1=px.size(),len2=py.size();
vector<vector<int> > row(len1+1, vector<int>(len2+1));
int i,j;
int result;
for(i=0;i<len1+1;i++) row[i][0]=i;
for(i=0;i<len2+1;i++) row[0][i]=i;
for(i=1;i<=len1;++i)
{
for(j=1;j<=len2;++j)
{
row[i][j] = min(min(
row[i-1][j-1] + ((px[i-1]==py[j-1])?0:1) , // replace
row[i][j-1] + 1), // delete
row[i-1][j] + 1 ); // insert
}
}
result=row[i-1][j-1];
return result;
}
};
int main()
{
cout<<LevenshteinDistance::edit("あい","あいう")<<endl;
return(0);
}結果は,
> c++ -o LevenshteinDistance edit0.cc ; ./LevenshteinDistance 3
当然,UTF-8の全角一文字分違うので,3バイト分ということで距離が3になってしまいます.
c++にもw_charとか多言語を意識した予約がありますが...今のところ,互換性等,信用できない...
というわけで,自前で.
#include <iostream>
#include <vector>
using namespace std;
class LevenshteinDistance
{
public:
static int isutf8(const string &c)
{
int ret;
if((c[0]&0x80) == 0x00) ret = 1;
else if((c[0]&0xe0) == 0xc0 && (c[1]&0xc0) == 0x80) ret = 2;
else if((c[0]&0xf0) == 0xe0 && (c[1]&0xc0) == 0x80
&& (c[2]&0xc0) == 0x80 ) ret = 3;
return(ret);
}
static bool equal(const string &s1, const string &s2)
{
bool ret = false;
if(isutf8(s1)==isutf8(s2))
{
int f=0;
for(int i=0;i<isutf8(s1);++i)
{
if(s1[i]==s2[i]) ++f;
}
if(f==isutf8(s1)) ret = true;
}
return(ret);
}
static int edit(const string &px, const string &py)
{
int len1=0,len2=0;
int i,j;
int result;
for(i=0;i<px.size();)
{
i += isutf8(&px[i]);
++len1;
}
for(i=0;i<py.size();)
{
i += isutf8(&py[i]);
++len2;
}
vector<vector<int> > row(len1+1, vector<int>(len2+1));
for(i=0;i<len1+1;i++) row[i][0]=i;
for(i=0;i<len2+1;i++) row[0][i]=i;
int k=0,l=0;
for(i=1;i<=len1;++i)
{
l = 0;
for(j=1;j<=len2;++j)
{
row[i][j] = min(min(
row[i-1][j-1] + (equal(&px[k],&py[l])?0:1) , // replace
row[i][j-1] + 1), // delete
row[i-1][j] + 1 ); // insert
l += isutf8(&py[l]);
}
k += isutf8(&px[k]);
}
result=row[i-1][j-1];
return result;
}
};
int main()
{
cout<<LevenshteinDistance::edit("あい","あいう")<<endl;
return(0);
}はい,実行すると
> c++ -o LevenshteinDistance edit.cc ; ./LevenshteinDistance 1
距離は1に.
せっかくなので,Javaのマルチバイト機能を信用しないバージョンを,C++から移植する形で書いてみました.
import java.io.*; import java.util.*; class LevenshteinDistance { static int isutf8(String args) throws UnsupportedEncodingException { int ret = -1; byte[] c = args.getBytes("UTF-8"); if((c[0]&0x80) == 0x00) ret = 1; else if((c[0]&0xe0) == 0xc0 && (c[1]&0xc0) == 0x80) ret = 2; else if((c[0]&0xf0) == 0xe0 && (c[1]&0xc0) == 0x80 && (c[2]&0xc0) == 0x80 ) ret = 3; return(ret); } static Boolean equal(String s1, String s2) throws UnsupportedEncodingException { Boolean ret = false; if(isutf8(s1)==isutf8(s2)) { int f=0; for(int i=0;i<isutf8(s1);++i) { byte[] c1 = s1.getBytes("UTF-8"); byte[] c2 = s2.getBytes("UTF-8"); if(c1[i]==c2[i]) ++f; } if(f==isutf8(s1)) ret = true; } return(ret); } static int edit(String px, String py) throws UnsupportedEncodingException { Vector<Vector> row = new Vector<Vector>(); int len1=0,len2=0; int i,j; int result; len1 = px.length(); len2 = py.length(); for(i=0; i<len1+1; ++i) { row.add(new Vector()); for(j=0; j<len2+1; ++j) { row.get(i).add(0); } } for(i=0;i<len1+1;i++) row.get(i).set(0,i); for(i=0;i<len2+1;i++) row.get(0).set(i,i); for(i=1;i<=len1;++i) { for(j=1;j<=len2;++j) { row.get(i).set(j,Math.min(Math.min( (Integer)(row.get(i-1).get(j-1)) + (equal(px.substring(i-1),py.substring(j-1))?0:1) , // replace (Integer)(row.get(i).get(j-1)) + 1), // delete (Integer)(row.get(i-1).get(j)) + 1 )); // insert } } result=(Integer)(row.get(len1).get(len2)); return result; } public static void main(String[] args) throws UnsupportedEncodingException { System.out.println(LevenshteinDistance.edit("あい","あいう")); } }
実行しますと,
> javac -encoding UTF-8 edit.java ; java -Dfile.encoding=UTF8 LevenshteinDistance Note: edit.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 1
一応,距離は1に.