github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/edit_distance.go (about)

     1  // Copyright 2011 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package nin
    16  
    17  func editDistance(s1, s2 string, allowReplacements bool, maxEditDistance int) int {
    18  	// The algorithm implemented below is the "classic"
    19  	// dynamic-programming algorithm for computing the Levenshtein
    20  	// distance, which is described here:
    21  	//
    22  	//   http://en.wikipedia.org/wiki/LevenshteinDistance
    23  	//
    24  	// Although the algorithm is typically described using an m x n
    25  	// array, only one row plus one element are used at a time, so this
    26  	// implementation just keeps one vector for the row.  To update one entry,
    27  	// only the entries to the left, top, and top-left are needed.  The left
    28  	// entry is in row[x-1], the top entry is what's in row[x] from the last
    29  	// iteration, and the top-left entry is stored in previous.
    30  	m := len(s1)
    31  	n := len(s2)
    32  
    33  	row := make([]int, n+1)
    34  	for i := 1; i <= n; i++ {
    35  		row[i] = i
    36  	}
    37  
    38  	for y := 1; y <= m; y++ {
    39  		row[0] = y
    40  		bestThisRow := row[0]
    41  
    42  		previous := y - 1
    43  		for x := 1; x <= n; x++ {
    44  			oldRow := row[x]
    45  			if allowReplacements {
    46  				v := 0
    47  				if s1[y-1] != s2[x-1] {
    48  					v = 1
    49  				}
    50  				row[x] = min(previous+v, min(row[x-1], row[x])+1)
    51  			} else {
    52  				if s1[y-1] == s2[x-1] {
    53  					row[x] = previous
    54  				} else {
    55  					row[x] = min(row[x-1], row[x]) + 1
    56  				}
    57  			}
    58  			previous = oldRow
    59  			bestThisRow = min(bestThisRow, row[x])
    60  		}
    61  
    62  		if maxEditDistance != 0 && bestThisRow > maxEditDistance {
    63  			return maxEditDistance + 1
    64  		}
    65  	}
    66  
    67  	return row[n]
    68  }
    69  
    70  func min(i, j int) int {
    71  	if i < j {
    72  		return i
    73  	}
    74  	return j
    75  }