├── README.md ├── go.mod ├── lsd.go └── lsd_test.go /README.md: -------------------------------------------------------------------------------- 1 | # go-lsd 2 | 3 | Levenshtein String Distance 4 | 5 | ## Usage 6 | 7 | ``` 8 | distnace := lsd.StringDistance("こんにちわ世界", "こにゃにゃちわ世界") // 3 9 | ``` 10 | 11 | ## Installation 12 | 13 | ``` 14 | $ go get github.com/mattn/go-lsd 15 | ``` 16 | 17 | ## License 18 | 19 | MIT 20 | 21 | ## Author 22 | 23 | Yasuhiro Matsumoto (a.k.a. mattn) 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-lsd 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /lsd.go: -------------------------------------------------------------------------------- 1 | package lsd 2 | 3 | func StringDistance(lhs, rhs string) int { 4 | return Distance([]rune(lhs), []rune(rhs)) 5 | } 6 | 7 | func Distance(lhs, rhs []rune) int { 8 | rl1, rl2 := len(lhs), len(rhs) 9 | 10 | // make distance from the root 11 | costs := make([]int, rl1+1) 12 | for j := 1; j <= rl1; j++ { 13 | costs[j] = j 14 | } 15 | 16 | cost, last, prev := 0, 0, 0 17 | for i := 1; i <= rl2; i++ { 18 | costs[0] = i 19 | last = i - 1 20 | for j := 1; j <= rl1; j++ { 21 | prev = costs[j] 22 | cost = 0 23 | // previous letter was different 24 | if lhs[j-1] != rhs[i-1] { 25 | cost = 1 26 | } 27 | if costs[j]+1 < costs[j-1]+1 { 28 | // changes to lhs should be minimum 29 | if costs[j]+1 < last+cost { 30 | costs[j] = costs[j] + 1 31 | } else { 32 | costs[j] = last + cost 33 | } 34 | } else { 35 | // changes to rhs should be minimum 36 | if costs[j-1]+1 < last+cost { 37 | costs[j] = costs[j-1] + 1 38 | } else { 39 | costs[j] = last + cost 40 | } 41 | } 42 | last = prev 43 | } 44 | } 45 | return costs[rl1] 46 | } 47 | -------------------------------------------------------------------------------- /lsd_test.go: -------------------------------------------------------------------------------- 1 | package lsd 2 | 3 | import "testing" 4 | 5 | func TestDistance(t *testing.T) { 6 | var tests = []struct { 7 | lhs string 8 | rhs string 9 | want int 10 | }{ 11 | {"こんにちわ世界", "こんにちわ世界", 0}, 12 | {"こんにちわ世界", "こにゃちわ世界", 2}, 13 | {"こんにちわ世界", "こにゃにゃちわ世界", 3}, 14 | {"こんにちわ世界", "こんばんわ世界", 2}, 15 | {"こんにちわ世界", "こんにちわ", 2}, 16 | {"こんにちわ世界", "こんばんわ", 4}, 17 | {"こんにちわ世界", "世界", 5}, 18 | } 19 | 20 | for _, test := range tests { 21 | got := StringDistance(test.lhs, test.rhs) 22 | if got != test.want { 23 | t.Fatalf("want %v but %v: %v vs %v", test.want, got, test.lhs, test.rhs) 24 | } 25 | } 26 | } 27 | --------------------------------------------------------------------------------