|
1 | 1 | """https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance""" |
2 | 2 |
|
| 3 | +from collections import defaultdict, deque |
| 4 | + |
3 | 5 |
|
4 | 6 | def jaro_winkler(str1: str, str2: str) -> float: |
5 | 7 | """ |
@@ -37,16 +39,23 @@ def jaro_winkler(str1: str, str2: str) -> float: |
37 | 39 |
|
38 | 40 | def get_matched_characters(_str1: str, _str2: str) -> str: |
39 | 41 | 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 | + |
41 | 46 | limit = min(len(_str1), len(_str2)) // 2 |
42 | 47 | for i, char in enumerate(_str1): |
| 48 | + positions = character_positions.get(char) |
| 49 | + if not positions: |
| 50 | + continue |
43 | 51 | left = int(max(0, i - limit)) |
44 | 52 | 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() |
50 | 59 |
|
51 | 60 | return "".join(matched) |
52 | 61 |
|
|
0 commit comments