Skip to content

Commit 9172815

Browse files
Optimize Jaro-Winkler matching with occurrence queues
Preserve earliest unused matches while avoiding repeated window scans. Retain all existing regression doctests and the benchmark.
1 parent ab709a2 commit 9172815

1 file changed

Lines changed: 15 additions & 6 deletions

File tree

‎strings/jaro_winkler.py‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance"""
22

3+
from collections import defaultdict, deque
4+
35

46
def jaro_winkler(str1: str, str2: str) -> float:
57
"""
@@ -37,16 +39,23 @@ def jaro_winkler(str1: str, str2: str) -> float:
3739

3840
def get_matched_characters(_str1: str, _str2: str) -> str:
3941
matched = []
40-
matched_indices: set[int] = set()
42+
character_positions: defaultdict[str, deque[int]] = defaultdict(deque)
43+
for index, char in enumerate(_str2):
44+
character_positions[char].append(index)
45+
4146
limit = min(len(_str1), len(_str2)) // 2
4247
for i, char in enumerate(_str1):
48+
positions = character_positions.get(char)
49+
if not positions:
50+
continue
4351
left = int(max(0, i - limit))
4452
right = int(min(i + limit + 1, len(_str2)))
45-
for index in range(left, right):
46-
if index not in matched_indices and char == _str2[index]:
47-
matched.append(char)
48-
matched_indices.add(index)
49-
break
53+
# Left edges only advance, so earlier positions cannot match later.
54+
while positions and positions[0] < left:
55+
positions.popleft()
56+
if positions and positions[0] < right:
57+
matched.append(char)
58+
positions.popleft()
5059

5160
return "".join(matched)
5261

0 commit comments

Comments
 (0)