Skip to content

Commit e90d759

Browse files
committed
optimize levenshtein to use a single rolling row
1 parent a16dce9 commit e90d759

1 file changed

Lines changed: 10 additions & 19 deletions

File tree

taskfile/suggest.go

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -87,39 +87,30 @@ func (r *Runner) suggestTasks(name string) []string {
8787

8888
// levenshtein returns the edit distance between a and b — the minimum number
8989
// of single-character insertions, deletions, or substitutions needed to turn
90-
// one into the other. The implementation uses two rolling rows so memory
91-
// stays O(min(len(a), len(b))) even for long task names.
90+
// one into the other. A single rolling row plus one scalar holding the
91+
// previous diagonal keeps memory at O(min(len(a), len(b))).
9292
func levenshtein(a, b string) int {
93-
ra := []rune(a)
94-
rb := []rune(b)
93+
ra, rb := []rune(a), []rune(b)
9594
if len(ra) < len(rb) {
9695
ra, rb = rb, ra
9796
}
98-
if len(rb) == 0 {
99-
return len(ra)
100-
}
10197

102-
prev := make([]int, len(rb)+1)
103-
curr := make([]int, len(rb)+1)
104-
for j := range prev {
105-
prev[j] = j
98+
row := make([]int, len(rb)+1)
99+
for j := range row {
100+
row[j] = j
106101
}
107102

108103
for i := 1; i <= len(ra); i++ {
109-
curr[0] = i
104+
diag := row[0] // dp[i-1][0] before we overwrite it
105+
row[0] = i
110106
for j := 1; j <= len(rb); j++ {
111107
cost := 1
112108
if ra[i-1] == rb[j-1] {
113109
cost = 0
114110
}
115-
curr[j] = min(
116-
prev[j]+1, // deletion
117-
curr[j-1]+1, // insertion
118-
prev[j-1]+cost, // substitution
119-
)
111+
diag, row[j] = row[j], min(row[j]+1, row[j-1]+1, diag+cost)
120112
}
121-
prev, curr = curr, prev
122113
}
123114

124-
return prev[len(rb)]
115+
return row[len(rb)]
125116
}

0 commit comments

Comments
 (0)