#include <string>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;
int levenshtein_distance(const std::string &s1, const std::string &s2)
{
int s1len = s1.size();
int s2len = s2.size();
auto column_start = (decltype(s1len))1;
auto column = new decltype(s1len)[s1len + 1];
iota(column + column_start, column + s1len + 1, column_start);
for (auto x = column_start; x <= s2len; x++) {
column[0] = x;
auto last_diagonal = x - column_start;
for (auto y = column_start; y <= s1len; y++) {
auto old_diagonal = column[y];
auto possibilities = {
column[y] + 1,
column[y - 1] + 1,
last_diagonal + (s1[y - 1] == s2[x - 1] ? 0 : 1)
};
column[y] = std::min(possibilities);
last_diagonal = old_diagonal;
}
}
auto result = column[s1len];
delete[] column;
return result;
}
#define H_G "\x01"
#define H_N "\x02"
#define H_D "\x03"
#define H_R "\x04"
#define H_M "\x05"
#define H_B "\x06"
#define H_S "\x07"
#define H_0 "\x08"
#define H_J "\x09"
#define H_C "\x0A"
#define H_K "\x0B"
#define H_T "\x0C"
#define H_P "\x0D"
#define H_H "\x0E"
#define H_A "\x10"
#define H_AE "\x11"
#define H_YA "\x12"
#define H_EO "\x13"
#define H_E "\x14"
#define H_YEO "\x15"
#define H_O "\x16"
#define H_YO "\x17"
#define H_U "\x18"
#define H_YU "\x19"
#define H_EU "\x1A"
#define H_I "\x1B"
#define H_SH "\x1F"
string disassembleKo2(const string& s)
{
static const char* mtbl1[] = { H_G, H_G H_SH,
H_N, H_D, H_D H_SH, H_R, H_M, H_B,
H_B H_SH, H_S, H_S H_SH, H_0, H_J, H_J H_SH,
H_C, H_K, H_T, H_P, H_H };
static const char* mtbl2[] = { H_A, H_AE, H_YA, H_AE H_SH,
H_EO, H_E, H_YEO, H_E H_SH,
H_O, H_O H_A, H_O H_AE, H_O H_I, H_YO,
H_U, H_U H_EO, H_U H_E, H_U H_I, H_YU,
H_EU, H_EU H_I, H_I };
static const char* mtbl3[] = { "", H_G, H_G H_SH, H_G H_S,
H_N, H_N H_J, H_N H_H, H_D,
H_R, H_R H_G, H_R H_M, H_R H_B, H_R H_S, H_R H_T, H_R H_P, H_R H_H,
H_M, H_B, H_B H_S, H_S, H_S H_SH, H_0, H_J,
H_C, H_K, H_T, H_P, H_H };
static const char* mtbl4[] = {H_G, H_G H_SH, H_G H_S,
H_N, H_N H_J, H_N H_H, H_D, H_D H_SH,
H_R, H_R H_G, H_R H_M, H_R H_B, H_R H_S, H_R H_T, H_R H_P, H_R H_H,
H_M, H_B, H_B H_SH, H_B H_S, H_S, H_S H_SH, H_0, H_J, H_J H_SH,
H_C, H_K, H_T, H_P, H_H };
string ret;
for (size_t i = 0; i < s.size(); i++)
{
if (s[i] & 0xE0 != 0xE0)
{
ret.push_back(s[i]);
continue;
}
if (i + 2 >= s.size()) break;
int chr = (s[i] & 0x0F) << 12;
chr |= (s[i + 1] & 0x3F) << 6;
chr |= (s[i + 2] & 0x3F);
if (chr >= 0xAC00 && chr < 0xD7B0)
{
chr -= 0xAC00;
int p1 = chr / (28 * 21),
p2 = (chr % (28 * 21)) / 28,
p3 = chr % 28;
ret.append(mtbl1[p1]);
ret.append(mtbl2[p2]);
if(p3) ret.append(mtbl3[p3]);
}
else if (chr >= 0x1100 && chr < 0x1200)
{
if (chr < 0x1100 + 19) ret.append(mtbl1[chr - 0x1100]);
else if (chr < 0x1161);
else if (chr < 0x1161 + 21) ret.append(mtbl2[chr - 0x1161]);
else if (chr <= 0x11A8);
else if (chr < 0x11A8 + 28) ret.append(mtbl2[chr - 0x11A8]);
}
else if (chr >= 0x3131 && chr < 0x319F)
{
if (chr < 0x3131 + 30) ret.append(mtbl4[chr - 0x3131]);
else if (chr < 0x314F + 21) ret.append(mtbl2[chr - 0x314F]);
}
else
{
ret.insert(ret.end(), &s[i], &s[i] + 3);
}
i += 2;
}
return ret;
}
int levenshtein_distance_map(const std::string & s1, const std::string & s2, const function<string(const string&)>& mapFunc)
{
string r1 = mapFunc(s1), r2 = mapFunc(s2);
return levenshtein_distance(r1, r2);
}
void test(const char* a, const char* b)
{
printf("%s - %s : %d (%d)\n", a, b,
levenshtein_distance_map(a, b, disassembleKo2),
levenshtein_distance(a, b));
}
int main()
{
test("없어", "ㅇ벗어");
test("빼고", "뺴고");
test("기다료", "기다려");
test("소고기", "소거기");
test("애이", "에이");
test("잇어", "있어");
test("그랬는데", "글샌ㄴ데");
return 0;
}
I2luY2x1ZGUgPHN0cmluZz4KI2luY2x1ZGUgPGFsZ29yaXRobT4KI2luY2x1ZGUgPG51bWVyaWM+CiNpbmNsdWRlIDxmdW5jdGlvbmFsPgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKCmludCBsZXZlbnNodGVpbl9kaXN0YW5jZShjb25zdCBzdGQ6OnN0cmluZyAmczEsIGNvbnN0IHN0ZDo6c3RyaW5nICZzMikKewoJaW50IHMxbGVuID0gczEuc2l6ZSgpOwoJaW50IHMybGVuID0gczIuc2l6ZSgpOwoKCWF1dG8gY29sdW1uX3N0YXJ0ID0gKGRlY2x0eXBlKHMxbGVuKSkxOwoKCWF1dG8gY29sdW1uID0gbmV3IGRlY2x0eXBlKHMxbGVuKVtzMWxlbiArIDFdOwoJaW90YShjb2x1bW4gKyBjb2x1bW5fc3RhcnQsIGNvbHVtbiArIHMxbGVuICsgMSwgY29sdW1uX3N0YXJ0KTsKCglmb3IgKGF1dG8geCA9IGNvbHVtbl9zdGFydDsgeCA8PSBzMmxlbjsgeCsrKSB7CgkJY29sdW1uWzBdID0geDsKCQlhdXRvIGxhc3RfZGlhZ29uYWwgPSB4IC0gY29sdW1uX3N0YXJ0OwoJCWZvciAoYXV0byB5ID0gY29sdW1uX3N0YXJ0OyB5IDw9IHMxbGVuOyB5KyspIHsKCQkJYXV0byBvbGRfZGlhZ29uYWwgPSBjb2x1bW5beV07CgkJCWF1dG8gcG9zc2liaWxpdGllcyA9IHsKCQkJCWNvbHVtblt5XSArIDEsCgkJCQljb2x1bW5beSAtIDFdICsgMSwKCQkJCWxhc3RfZGlhZ29uYWwgKyAoczFbeSAtIDFdID09IHMyW3ggLSAxXSA/IDAgOiAxKQoJCQl9OwoJCQljb2x1bW5beV0gPSBzdGQ6Om1pbihwb3NzaWJpbGl0aWVzKTsKCQkJbGFzdF9kaWFnb25hbCA9IG9sZF9kaWFnb25hbDsKCQl9Cgl9CglhdXRvIHJlc3VsdCA9IGNvbHVtbltzMWxlbl07CglkZWxldGVbXSBjb2x1bW47CglyZXR1cm4gcmVzdWx0Owp9CgojZGVmaW5lIEhfRyAiXHgwMSIKI2RlZmluZSBIX04gIlx4MDIiCiNkZWZpbmUgSF9EICJceDAzIgojZGVmaW5lIEhfUiAiXHgwNCIKI2RlZmluZSBIX00gIlx4MDUiCiNkZWZpbmUgSF9CICJceDA2IgojZGVmaW5lIEhfUyAiXHgwNyIKI2RlZmluZSBIXzAgIlx4MDgiCiNkZWZpbmUgSF9KICJceDA5IgojZGVmaW5lIEhfQyAiXHgwQSIKI2RlZmluZSBIX0sgIlx4MEIiCiNkZWZpbmUgSF9UICJceDBDIgojZGVmaW5lIEhfUCAiXHgwRCIKI2RlZmluZSBIX0ggIlx4MEUiCgojZGVmaW5lIEhfQSAiXHgxMCIKI2RlZmluZSBIX0FFICJceDExIgojZGVmaW5lIEhfWUEgIlx4MTIiCiNkZWZpbmUgSF9FTyAiXHgxMyIKI2RlZmluZSBIX0UgIlx4MTQiCiNkZWZpbmUgSF9ZRU8gIlx4MTUiCiNkZWZpbmUgSF9PICJceDE2IgojZGVmaW5lIEhfWU8gIlx4MTciCiNkZWZpbmUgSF9VICJceDE4IgojZGVmaW5lIEhfWVUgIlx4MTkiCiNkZWZpbmUgSF9FVSAiXHgxQSIKI2RlZmluZSBIX0kgIlx4MUIiCgojZGVmaW5lIEhfU0ggIlx4MUYiCgpzdHJpbmcgZGlzYXNzZW1ibGVLbzIoY29uc3Qgc3RyaW5nJiBzKQp7CglzdGF0aWMgY29uc3QgY2hhciogbXRibDFbXSA9IHsgSF9HLCBIX0cgSF9TSCwKCQlIX04sIEhfRCwgSF9EIEhfU0gsIEhfUiwgSF9NLCBIX0IsCgkJSF9CIEhfU0gsIEhfUywgSF9TIEhfU0gsIEhfMCwgSF9KLCBIX0ogSF9TSCwKCQlIX0MsIEhfSywgSF9ULCBIX1AsIEhfSCB9OwoKCXN0YXRpYyBjb25zdCBjaGFyKiBtdGJsMltdID0geyBIX0EsIEhfQUUsIEhfWUEsIEhfQUUgSF9TSCwKCQlIX0VPLCBIX0UsIEhfWUVPLCBIX0UgSF9TSCwKCQlIX08sIEhfTyBIX0EsIEhfTyBIX0FFLCBIX08gSF9JLCBIX1lPLAoJCUhfVSwgSF9VIEhfRU8sIEhfVSBIX0UsIEhfVSBIX0ksIEhfWVUsCgkJSF9FVSwgSF9FVSBIX0ksIEhfSSB9OwoKCXN0YXRpYyBjb25zdCBjaGFyKiBtdGJsM1tdID0geyAiIiwgSF9HLCBIX0cgSF9TSCwgSF9HIEhfUywKCQlIX04sIEhfTiBIX0osIEhfTiBIX0gsIEhfRCwKCQlIX1IsIEhfUiBIX0csIEhfUiBIX00sIEhfUiBIX0IsIEhfUiBIX1MsIEhfUiBIX1QsIEhfUiBIX1AsIEhfUiBIX0gsCgkJSF9NLCBIX0IsIEhfQiBIX1MsIEhfUywgSF9TIEhfU0gsIEhfMCwgSF9KLAoJCUhfQywgSF9LLCBIX1QsIEhfUCwgSF9IIH07CgoJc3RhdGljIGNvbnN0IGNoYXIqIG10Ymw0W10gPSB7SF9HLCBIX0cgSF9TSCwgSF9HIEhfUywKCQlIX04sIEhfTiBIX0osIEhfTiBIX0gsIEhfRCwgSF9EIEhfU0gsCgkJSF9SLCBIX1IgSF9HLCBIX1IgSF9NLCBIX1IgSF9CLCBIX1IgSF9TLCBIX1IgSF9ULCBIX1IgSF9QLCBIX1IgSF9ILAoJCUhfTSwgSF9CLCBIX0IgSF9TSCwgSF9CIEhfUywgSF9TLCBIX1MgSF9TSCwgSF8wLCBIX0osIEhfSiBIX1NILAoJCUhfQywgSF9LLCBIX1QsIEhfUCwgSF9IIH07CgoJc3RyaW5nIHJldDsKCWZvciAoc2l6ZV90IGkgPSAwOyBpIDwgcy5zaXplKCk7IGkrKykKCXsKCQlpZiAoc1tpXSAmIDB4RTAgIT0gMHhFMCkKCQl7CgkJCXJldC5wdXNoX2JhY2soc1tpXSk7CgkJCWNvbnRpbnVlOwoJCX0KCQlpZiAoaSArIDIgPj0gcy5zaXplKCkpIGJyZWFrOwoKCQlpbnQgY2hyID0gKHNbaV0gJiAweDBGKSA8PCAxMjsKCQljaHIgfD0gKHNbaSArIDFdICYgMHgzRikgPDwgNjsKCQljaHIgfD0gKHNbaSArIDJdICYgMHgzRik7CgoJCWlmIChjaHIgPj0gMHhBQzAwICYmIGNociA8IDB4RDdCMCkKCQl7CgkJCWNociAtPSAweEFDMDA7CgkJCWludCBwMSA9IGNociAvICgyOCAqIDIxKSwKCQkJCXAyID0gKGNociAlICgyOCAqIDIxKSkgLyAyOCwKCQkJCXAzID0gY2hyICUgMjg7CgkJCXJldC5hcHBlbmQobXRibDFbcDFdKTsKCQkJcmV0LmFwcGVuZChtdGJsMltwMl0pOwoJCQlpZihwMykgcmV0LmFwcGVuZChtdGJsM1twM10pOwoJCX0KCQllbHNlIGlmIChjaHIgPj0gMHgxMTAwICYmIGNociA8IDB4MTIwMCkKCQl7CgkJCWlmIChjaHIgPCAweDExMDAgKyAxOSkgcmV0LmFwcGVuZChtdGJsMVtjaHIgLSAweDExMDBdKTsKCQkJZWxzZSBpZiAoY2hyIDwgMHgxMTYxKTsKCQkJZWxzZSBpZiAoY2hyIDwgMHgxMTYxICsgMjEpIHJldC5hcHBlbmQobXRibDJbY2hyIC0gMHgxMTYxXSk7CgkJCWVsc2UgaWYgKGNociA8PSAweDExQTgpOwoJCQllbHNlIGlmIChjaHIgPCAweDExQTggKyAyOCkgcmV0LmFwcGVuZChtdGJsMltjaHIgLSAweDExQThdKTsKCQl9CgkJZWxzZSBpZiAoY2hyID49IDB4MzEzMSAmJiBjaHIgPCAweDMxOUYpCgkJewoJCQlpZiAoY2hyIDwgMHgzMTMxICsgMzApIHJldC5hcHBlbmQobXRibDRbY2hyIC0gMHgzMTMxXSk7CgkJCWVsc2UgaWYgKGNociA8IDB4MzE0RiArIDIxKSByZXQuYXBwZW5kKG10YmwyW2NociAtIDB4MzE0Rl0pOwoJCX0KCQllbHNlCgkJewoJCQlyZXQuaW5zZXJ0KHJldC5lbmQoKSwgJnNbaV0sICZzW2ldICsgMyk7CgkJfQoJCWkgKz0gMjsKCX0KCXJldHVybiByZXQ7Cn0KCmludCBsZXZlbnNodGVpbl9kaXN0YW5jZV9tYXAoY29uc3Qgc3RkOjpzdHJpbmcgJiBzMSwgY29uc3Qgc3RkOjpzdHJpbmcgJiBzMiwgY29uc3QgZnVuY3Rpb248c3RyaW5nKGNvbnN0IHN0cmluZyYpPiYgbWFwRnVuYykKewoJc3RyaW5nIHIxID0gbWFwRnVuYyhzMSksIHIyID0gbWFwRnVuYyhzMik7CglyZXR1cm4gbGV2ZW5zaHRlaW5fZGlzdGFuY2UocjEsIHIyKTsKfQoKdm9pZCB0ZXN0KGNvbnN0IGNoYXIqIGEsIGNvbnN0IGNoYXIqIGIpCnsKCXByaW50ZigiJXMgLSAlcyA6ICVkICglZClcbiIsIGEsIGIsCgkJbGV2ZW5zaHRlaW5fZGlzdGFuY2VfbWFwKGEsIGIsIGRpc2Fzc2VtYmxlS28yKSwgCgkJbGV2ZW5zaHRlaW5fZGlzdGFuY2UoYSwgYikpOwp9CgppbnQgbWFpbigpIAp7Cgl0ZXN0KCLsl4bslrQiLCAi44WH67KX7Ja0Iik7Cgl0ZXN0KCLrubzqs6AiLCAi67q06rOgIik7Cgl0ZXN0KCLquLDri6Tro4wiLCAi6riw64uk66CkIik7Cgl0ZXN0KCLshozqs6DquLAiLCAi7IaM6rGw6riwIik7Cgl0ZXN0KCLslaDsnbQiLCAi7JeQ7J20Iik7Cgl0ZXN0KCLsnofslrQiLCAi7J6I7Ja0Iik7Cgl0ZXN0KCLqt7jrnqzripTrjbAiLCAi6riA7IOM44S0642wIik7CglyZXR1cm4gMDsKfQo=