|
25 | 25 |
|
26 | 26 | import info.debatty.java.stringsimilarity.interfaces.MetricStringDistance; |
27 | 27 | import java.util.HashMap; |
28 | | -import info.debatty.java.stringsimilarity.interfaces.StringDistance; |
| 28 | +import net.jcip.annotations.Immutable; |
29 | 29 |
|
30 | 30 | /** |
31 | | - * Implementation of Damerau-Levenshtein distance with transposition (also |
| 31 | + * Implementation of Damerau-Levenshtein distance with transposition (also |
32 | 32 | * sometimes calls unrestricted Damerau-Levenshtein distance). |
33 | | - * It is the minimum number of operations needed to transform one string into |
34 | | - * the other, where an operation is defined as an insertion, deletion, or |
35 | | - * substitution of a single character, or a transposition of two adjacent |
| 33 | + * It is the minimum number of operations needed to transform one string into |
| 34 | + * the other, where an operation is defined as an insertion, deletion, or |
| 35 | + * substitution of a single character, or a transposition of two adjacent |
36 | 36 | * characters. |
37 | 37 | * It does respect triangle inequality, and is thus a metric distance. |
38 | 38 | * |
|
41 | 41 | * |
42 | 42 | * @author Thibault Debatty |
43 | 43 | */ |
44 | | -public class Damerau implements StringDistance, MetricStringDistance { |
45 | | - |
46 | | - public double distance(String s1, String s2) { |
| 44 | +@Immutable |
| 45 | +public class Damerau implements MetricStringDistance { |
| 46 | + |
| 47 | + /** |
| 48 | + * Compute the distance between strings: the minimum number of operations |
| 49 | + * needed to transform one string into the other (insertion, deletion, |
| 50 | + * substitution of a single character, or a transposition of two adjacent |
| 51 | + * characters). |
| 52 | + * @param s1 |
| 53 | + * @param s2 |
| 54 | + * @return |
| 55 | + */ |
| 56 | + public final double distance(final String s1, final String s2) { |
47 | 57 |
|
48 | 58 | // INFinite distance is the max possible distance |
49 | | - int INF = s1.length() + s2.length(); |
| 59 | + int inf = s1.length() + s2.length(); |
50 | 60 |
|
51 | 61 | // Create and initialize the character array indices |
52 | | - HashMap<Character, Integer> DA = new HashMap<Character, Integer>(); |
| 62 | + HashMap<Character, Integer> da = new HashMap<Character, Integer>(); |
53 | 63 |
|
54 | 64 | for (int d = 0; d < s1.length(); d++) { |
55 | | - if (!DA.containsKey(s1.charAt(d))) { |
56 | | - DA.put(s1.charAt(d), 0); |
| 65 | + if (!da.containsKey(s1.charAt(d))) { |
| 66 | + da.put(s1.charAt(d), 0); |
57 | 67 | } |
58 | 68 | } |
59 | 69 |
|
60 | 70 | for (int d = 0; d < s2.length(); d++) { |
61 | | - if (!DA.containsKey(s2.charAt(d))) { |
62 | | - DA.put(s2.charAt(d), 0); |
| 71 | + if (!da.containsKey(s2.charAt(d))) { |
| 72 | + da.put(s2.charAt(d), 0); |
63 | 73 | } |
64 | 74 | } |
65 | 75 |
|
66 | 76 | // Create the distance matrix H[0 .. s1.length+1][0 .. s2.length+1] |
67 | | - int[][] H = new int[s1.length() + 2][s2.length() + 2]; |
| 77 | + int[][] h = new int[s1.length() + 2][s2.length() + 2]; |
68 | 78 |
|
69 | 79 | // initialize the left and top edges of H |
70 | 80 | for (int i = 0; i <= s1.length(); i++) { |
71 | | - H[i + 1][0] = INF; |
72 | | - H[i + 1][1] = i; |
| 81 | + h[i + 1][0] = inf; |
| 82 | + h[i + 1][1] = i; |
73 | 83 | } |
74 | 84 |
|
75 | 85 | for (int j = 0; j <= s2.length(); j++) { |
76 | | - H[0][j + 1] = INF; |
77 | | - H[1][j + 1] = j; |
| 86 | + h[0][j + 1] = inf; |
| 87 | + h[1][j + 1] = j; |
78 | 88 |
|
79 | 89 | } |
80 | 90 |
|
81 | 91 | // fill in the distance matrix H |
82 | 92 | // look at each character in s1 |
83 | 93 | for (int i = 1; i <= s1.length(); i++) { |
84 | | - int DB = 0; |
| 94 | + int db = 0; |
85 | 95 |
|
86 | 96 | // look at each character in b |
87 | 97 | for (int j = 1; j <= s2.length(); j++) { |
88 | | - int i1 = DA.get(s2.charAt(j - 1)); |
89 | | - int j1 = DB; |
| 98 | + int i1 = da.get(s2.charAt(j - 1)); |
| 99 | + int j1 = db; |
90 | 100 |
|
91 | 101 | int cost = 1; |
92 | 102 | if (s1.charAt(i - 1) == s2.charAt(j - 1)) { |
93 | 103 | cost = 0; |
94 | | - DB = j; |
| 104 | + db = j; |
95 | 105 | } |
96 | 106 |
|
97 | | - H[i + 1][j + 1] = min( |
98 | | - H[i][j] + cost, // substitution |
99 | | - H[i + 1][j] + 1, // insertion |
100 | | - H[i][j + 1] + 1, // deletion |
101 | | - H[i1][j1] + (i - i1 - 1) + 1 + (j - j1 - 1)); |
| 107 | + h[i + 1][j + 1] = min( |
| 108 | + h[i][j] + cost, // substitution |
| 109 | + h[i + 1][j] + 1, // insertion |
| 110 | + h[i][j + 1] + 1, // deletion |
| 111 | + h[i1][j1] + (i - i1 - 1) + 1 + (j - j1 - 1)); |
102 | 112 | } |
103 | 113 |
|
104 | | - DA.put(s1.charAt(i - 1), i); |
| 114 | + da.put(s1.charAt(i - 1), i); |
105 | 115 | } |
106 | 116 |
|
107 | | - return H[s1.length() + 1][s2.length() + 1]; |
| 117 | + return h[s1.length() + 1][s2.length() + 1]; |
108 | 118 | } |
109 | 119 |
|
110 | | - protected static int min(int a, int b, int c, int d) { |
| 120 | + private static int min( |
| 121 | + final int a, final int b, final int c, final int d) { |
111 | 122 | return Math.min(a, Math.min(b, Math.min(c, d))); |
112 | 123 | } |
113 | 124 |
|
|
0 commit comments