프론트엔드 개발자로서, 우리는 사용자의 오타를 교정하거나 비슷한 단어를 찾는 기능을 종종 구현한다.
예를 들어, 검색 창에서 "javascipt"라고 입력하면 "javascript"를 추천해주는 기능은 흔하게 찾을 수 있다 👍
(r이 빠졌는데도 찾아준다)
이처럼 두 문자열의 차이를 계산하는 알고리즘 중 하나가 바로 레벤슈타인 거리(Levenshtein Distance)입니다.
이번 글에서는 레벤슈타인 거리란 무엇인지, 그리고 이를 타입스크립트로 구현하는 방법을 단계별로 알아보았다.
리벤슈타인 거리란?
리벤슈타인 거리는 두 문자열 사이의 최소 편집 거리를 계산하는 알고리즘이다.
여기서 [편집]은 다음 세 가지 작업을 의미한다.
- 삽입 (Insertion): 문자 하나를 추가
- 삭제 (Deletion): 문자 하나를 제거
- 교체 (Substitution): 문자 하나를 다른 문자로 대체
예를 들어:
- "kitten" -> "sitting"의 리벤슈타인 거리는 3이다.
- "k"를 "s"로 교체.
- "e"를 "i"로 교체.
- 마지막에 "g"를 삽입.
리벤슈타인 거리 계산 방법
리벤슈타인 거리를 계산하기 위해 우리는 2차원 배열을 사용하여 동적 프로그래밍(Dynamic Programming) 방식으로 문제를 풀 수 있다.
단계별로 접근해보자 ! 🙌
- 문제
두 문자열 A와 B가 주어졌을 때, A를 B로 변환하는 최소 편집 거리를 구한다. - 조건
빈 문자열을 다른 문자열로 변환하려면, 삽입이나 삭제 작업만 필요하다. - 아이디어
두 문자가 같다면, 이전 값 그대로 가져온다.
다르다면 삽입, 삭제, 교체 중 최소 비용을 선택한다.
공식은 다음과 같다.
타입스크립트로 구현하기 🍟
이제 직접 타입스크립트로 리벤슈타인 거리 알고리즘을 구현해 보자.
function levenshteinDistance(a: string, b: string): number {
const m = a.length;
const n = b.length;
// 2차원 배열 생성
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
// 초기 값 설정
for (let i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (let j = 0; j <= n; j++) {
dp[0][j] = j;
}
// 동적 프로그래밍 계산
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (a[i - 1] === b[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + Math.min(
dp[i - 1][j], // 삭제
dp[i][j - 1], // 삽입
dp[i - 1][j - 1] // 교체
);
}
}
}
return dp[m][n];
}
// 테스트
console.log(levenshteinDistance("kitten", "sitting")); // 3
console.log(levenshteinDistance("flaw", "lawn")); // 2
작성한 코드는 위와 같다 ❗
- 2차원 배열 생성
dp는 편집 거리를 저장하는 테이블로,
dp[i][j]는 문자열 A의 처음 i개 문자와 문자열 B의 처음 j개 문자의 최소 편집 거리를 나타낸다. - 초기 값 설정
첫 번째 행과 첫 번째 열은 빈 문자열과 비교한 결과로, 인덱스 값으로 초기화한다. - 동적 프로그래밍 계산
두 문자가 같으면 이전 값(dp[i-1][j-1])을 그대로 가져온다.
다르면 삽입, 삭제, 교체 중 최소 값을 선택하고 1을 더한다. - 결과 반환
최종적으로 dp[m][n]에 두 문자열의 최소 편집 거리가 저장된다.
이 알고리즘은 실제로는 어디에 사용될 수 있을까 ❓
오타 교정 기능
레벤슈타인 거리 알고리즘을 사용하여 사용자의 입력값과 사전에 저장된 단어들 간의 편집 거리를 계산하면, 가장 비슷한 단어를 추천할 수 있다.
function suggestWord(input: string, dictionary: string[]): string {
let closestWord = "";
let minDistance = Infinity;
for (const word of dictionary) {
const distance = levenshteinDistance(input, word);
if (distance < minDistance) {
minDistance = distance;
closestWord = word;
}
}
return closestWord;
}
// 테스트
const dictionary = ["javascript", "typescript", "java", "python"];
console.log(suggestWord("javascipt", dictionary)); // "javascript"
👻 위에서 사용한 예제와 비슷하게 정해진 List 내에서 검색을 할 때 검색어와 List 내부의 후보들의 유사성을 확인한 뒤 사용자에게 결과를 추천해줄 수도 있다 !
레벤슈타인 거리는 문자열 유사도를 계산하는 강력한 알고리즘으로, 프론트엔드 개발에서도 유용하게 활용할 수 있다.
특히 검색, 자동 완성, 오타 교정 등에서 큰 역할을 하기에, 알아두면 좋을 것 같다 🥝
+ 개인적으로 이번 글을 통해 타입스크립트를 사용해 구현하면서 오랜만에 알고리즘에 대해서 생각해볼 수 있었다!
😊 참고 자료
https://en.wikipedia.org/wiki/Levenshtein_distance
Levenshtein distance - Wikipedia
From Wikipedia, the free encyclopedia Computer science metric for string similarity Levenshtein distanceEdit distance matrix for two words using cost of substitution as 1 and cost of deletion or insertion as 0.5Classmeasuring the difference between two seq
en.wikipedia.org
https://www.npmjs.com/package/kled
kled
Fuzzy Matching Library with Levenshtein Edit Distance, Tailored for Korean Language Support. Latest version: 0.1.6, last published: a year ago. Start using kled in your project by running `npm i kled`. There is 1 other project in the npm registry using kle
www.npmjs.com
'웹 개발' 카테고리의 다른 글
Phantom Dependency에 대해서 알아보고 대비책 생각해보기 👻 (0) | 2025.01.14 |
---|---|
FE 개발에서 Single Source of Truth (SSOT) 알아보기 👍 (0) | 2025.01.02 |
[Javascript] 이벤트의 실행순서 파악하기 (onMouseDown은 onClick보다 먼저 실행된다 👻) (0) | 2024.12.21 |
[javascript 안전한 이벤트] event.isTrusted를 활용해 이벤트 보안과 무결성 지키기 (1) | 2024.12.01 |
bfcache를 사용하면, 브라우저 속도 최적화를 할 수 있다 ! 👻 (1) | 2024.11.20 |