1- package com .mycmd ;
1+ package com .mycmd . utils ;
22
3- import java .util .List ;
3+ import java .util .Collection ;
4+ import java .util .Objects ;
45
5- public class StringUtils {
6- // Compute Levenshtein distance between two strings
7- public static int levenshtein (String a , String b ) {
8- int [] costs = new int [b .length () + 1 ];
9- for (int j = 0 ; j < costs .length ; j ++) {
10- costs [j ] = j ;
6+ /**
7+ * Small utility for string-based helper methods.
8+ * Provides a findClosest(...) implementation using Levenshtein distance.
9+ */
10+ public final class StringUtils {
11+
12+ private StringUtils () {}
13+
14+ /**
15+ * Find the closest string in candidates to the input.
16+ * Returns null when no candidate is close enough.
17+ *
18+ * @param input the input string
19+ * @param candidates candidate strings
20+ * @return the closest candidate or null
21+ */
22+ public static String findClosest (String input , Collection <String > candidates ) {
23+ if (input == null || input .isEmpty () || candidates == null || candidates .isEmpty ()) {
24+ return null ;
1125 }
12- for (int i = 1 ; i <= a .length (); i ++) {
13- costs [0 ] = i ;
14- int nw = i - 1 ;
15- for (int j = 1 ; j <= b .length (); j ++) {
16- int cj = Math .min (1 + Math .min (costs [j ], costs [j - 1 ]), a .charAt (i - 1 ) == b .charAt (j - 1 ) ? nw : nw + 1 );
17- nw = costs [j ];
18- costs [j ] = cj ;
26+
27+ String best = null ;
28+ int bestDistance = Integer .MAX_VALUE ;
29+
30+ for (String cand : candidates ) {
31+ if (cand == null || cand .isEmpty ()) continue ;
32+ if (cand .equalsIgnoreCase (input )) {
33+ // exact match ignoring case - return immediately
34+ return cand ;
1935 }
36+ int dist = levenshteinDistance (input .toLowerCase (), cand .toLowerCase ());
37+ if (dist < bestDistance ) {
38+ bestDistance = dist ;
39+ best = cand ;
40+ }
41+ }
42+
43+ // Choose a threshold: allow suggestion only when distance is reasonably small.
44+ // Here we allow suggestions when distance <= max(1, input.length()/3)
45+ int threshold = Math .max (1 , input .length () / 3 );
46+ if (bestDistance <= threshold ) {
47+ return best ;
2048 }
21- return costs [ b . length ()] ;
49+ return null ;
2250 }
2351
24- // Find the closest string from a list
25- public static String findClosest (String input , List <String > candidates ) {
26- String closest = null ;
27- int minDist = Integer .MAX_VALUE ;
28- for (String candidate : candidates ) {
29- int dist = levenshtein (input , candidate );
30- if (dist < minDist ) {
31- minDist = dist ;
32- closest = candidate ;
52+ // Classic iterative Levenshtein algorithm (memory O(min(m,n)))
53+ private static int levenshteinDistance (String a , String b ) {
54+ if (Objects .equals (a , b )) return 0 ;
55+ if (a .length () == 0 ) return b .length ();
56+ if (b .length () == 0 ) return a .length ();
57+
58+ // ensure a is the shorter
59+ if (a .length () > b .length ()) {
60+ String tmp = a ;
61+ a = b ;
62+ b = tmp ;
63+ }
64+
65+ int [] prev = new int [a .length () + 1 ];
66+ int [] curr = new int [a .length () + 1 ];
67+
68+ for (int i = 0 ; i <= a .length (); i ++) prev [i ] = i ;
69+
70+ for (int j = 1 ; j <= b .length (); j ++) {
71+ curr [0 ] = j ;
72+ char bj = b .charAt (j - 1 );
73+ for (int i = 1 ; i <= a .length (); i ++) {
74+ int cost = (a .charAt (i - 1 ) == bj ) ? 0 : 1 ;
75+ curr [i ] = min3 (curr [i - 1 ] + 1 , prev [i ] + 1 , prev [i - 1 ] + cost );
3376 }
77+ // swap prev and curr
78+ int [] tmp = prev ;
79+ prev = curr ;
80+ curr = tmp ;
3481 }
35- return closest ;
82+ return prev [a .length ()];
83+ }
84+
85+ private static int min3 (int x , int y , int z ) {
86+ return Math .min (x , Math .min (y , z ));
3687 }
3788}
0 commit comments