How to Measure String Similarity: Edit Distance and Fuzzy Matching
Learn how Levenshtein distance, similarity ratios, and Jaro-Winkler power fuzzy matching, deduplication, typo detection, and record linkage in real projects.
How to Measure String Similarity Without Guessing
"Are these two strings the same thing?" sounds like a yes-or-no question, but real data never cooperates. A customer types gmial.com instead of gmail.com. One spreadsheet has Jon Smith, the other John Smith. A search box receives reciever when the catalog says receiver. To handle any of this you need a number that says how close two strings are, not just whether they match byte for byte. That number is what string similarity gives you, and there is more than one honest way to compute it.
This post walks through the core idea behind edit distance, shows how a raw count becomes a percentage you can threshold against, and covers where each metric earns its keep: fuzzy matching, deduplication, typo detection, and record linkage. If you want to follow along, paste any two strings into the String Similarity Checker and watch four metrics update side by side.
What Levenshtein Distance Actually Counts
The Levenshtein distance is the minimum number of single-character edits needed to transform one string into another, where an edit is one of three operations: insert a character, delete a character, or substitute one character for another. That is the whole definition. It counts edits, nothing else.
The textbook example is turning kitten into sitting. The cheapest path is three edits:
- Substitute
k→s(givessitten) - Substitute
e→i(givessittin) - Insert
gat the end (givessitting)
So the Levenshtein distance is 3. The key word in the definition is minimum. A good implementation fills a dynamic-programming grid to guarantee it found the cheapest path, never an over-count from a greedy guess that committed to a bad substitution early. That guarantee matters once you start thresholding: an inflated distance silently rejects pairs that should have matched.
Turning a Distance Into a Similarity Ratio
A raw distance of 3 is meaningless on its own. Is that close or far? It depends entirely on how long the strings are. Three edits between two four-letter words is a wreck; three edits between two 200-character paragraphs is a rounding error. So you normalize.
The standard formula divides the distance by the length of the longer string and subtracts from one:
similarity = 1 - distance / maxLength
Worked through on kitten vs sitting: the distance is 3 and the longer string is 7 characters, so the similarity is 1 - 3/7 ≈ 0.571, or about 57%. Two identical strings have a distance of 0 and score 100%; two strings with nothing in common drift toward 0%. Now the number is comparable across short and long inputs, which is exactly what you need before you can say "treat anything above 90% as a likely match."
This is the single most common mistake I see: people compare raw distances across pairs of wildly different length and wonder why their threshold behaves erratically. Always normalize first.
One Size Does Not Fit Every Job
Edit distance is position-aware, which is its strength and its weakness. Reorder the words in a sentence and the Levenshtein score collapses even though a human reads them as nearly identical. That is why serious similarity work uses more than one metric:
- Dice coefficient counts shared adjacent character pairs (bigrams) as
2 × shared bigrams / total bigrams. It ignores word order, so it shines on titles, product names, and lightly paraphrased copy. - Jaro-Winkler is tuned for short strings like personal names and rewards a matching prefix, on the theory that typos near the start are rare and therefore meaningful.
MARTHAvsMARHTAscores about 0.96 because only two letters transpose and theMARprefix is intact. - Longest common subsequence (LCS) reports the length of the longest run two strings share in order, which separates a light paraphrase from a wholesale copy.
The rule of thumb: names favor Jaro-Winkler, source code favors Levenshtein, and loose phrasing favors Dice. Picking the wrong metric is how you get a misleadingly low score on data that is actually a match.
Fuzzy Matching, Deduplication, and Typo Detection
These three jobs are the bread and butter of similarity work, and they all reduce to the same loop: score each candidate, then threshold.
Typo detection is the cleanest case. A quiz key says photosynthesis and a student wrote photosynthesus. The Levenshtein distance is 1, the similarity sits well above 90%, and you can award credit for a near-miss instead of marking it wrong. The same logic catches gmail.com vs gmial.com in a signup list (distance 2) so a human can merge them.
Fuzzy search scores a query against each catalog entry and ranks by similarity. When someone searches reciever, Levenshtein handles the single transposition and the real receiver floats to the top. A forgiving search box is just this loop wrapped in a UI.
Deduplication runs the same scoring over a list against itself. Set a threshold (0.9 is a sane starting point for names), and any pair above it gets flagged for review or auto-merged. If you are deduplicating freeform text rather than short labels, run your candidates through the Text Deduplicator first to strip exact repeats, then use similarity scoring to catch the near-duplicates that exact matching misses.
Record Linkage at Scale
Record linkage is deduplication's grown-up cousin: deciding whether two records from different systems refer to the same real-world entity. A CRM export and a billing export both list customers, but the names, addresses, and casing never line up perfectly.
Here similarity metrics become features in a decision. You might score the name with Jaro-Winkler, the email with Levenshtein, and the street address with Dice, then combine the three into a confidence value. Pairs above an upper threshold auto-merge; pairs below a lower one stay separate; the band in between goes to a human. Two practical settings make or break this: turn case sensitivity off so McDonald and mcdonald count as identical, and decide deliberately whether whitespace matters, because a stray double space should not inflate a name's distance.
When I last cleaned a contact list this way, the surprise was not the obvious typos. It was how many "different" records were the same person with a middle initial added in one system and dropped in the other. A pure exact match found none of them; a Jaro-Winkler threshold of 0.9 surfaced the whole cluster in one pass, and I spent the afternoon merging instead of hunting.
Putting It Together
String similarity is not one algorithm but a small toolkit, and the skill is matching the metric to the data. Levenshtein counts edits and respects position; Dice forgives word order; Jaro-Winkler rewards a shared prefix on short names; LCS measures the longest shared run. Normalize every distance into a percentage before you trust it, choose your case and whitespace settings on purpose, and threshold against a number that means the same thing whether the strings are six characters or six hundred.
When you want to see the exact characters that differ rather than a single score, pair this with the Text Diff tool, then come back to the String Similarity Checker to put a percentage on how close they really are.
Made by Toolora · Updated 2026-06-13