코딩테스트 JS 해시 풀이: 모든 아나그램 찾기

2026.05.07·수정 2026.09.13·약 9분·작성: 해비·블로그 소개

주요 포인트 한눈에 보기

문자열 아나그램 문제는 해시(Map)와 슬라이딩 윈도우를 함께 활용하는 대표적인 코딩테스트 유형입니다.
이 글에서는 직접 풀지 못한 상태에서 문제를 어떻게 분석하고, 왜 이 풀이 구조가 나오는지 흐름 중심으로 정리합니다.

문제

코딩테스트 JS 해시 풀이: 모든 아나그램 찾기 전반부 흐름 정리

S문자열에서 T문자열과 아나그램이 되는 S의 부분문자열의 개수를 구하는 프로그램을 작성하는 문제입니다.
아나그램 판별 시 대소문자는 서로 다른 문자로 구분되며, 부분문자열은 반드시 연속된 문자열이어야 합니다.

첫 번째 줄에는 문자열 S가 주어지고, 두 번째 줄에는 문자열 T가 주어집니다.
S문자열의 길이는 최대 10,000을 넘지 않으며, T문자열의 길이는 S문자열의 길이보다 작거나 같습니다.

출력은 S단어 안에서 T문자열과 아나그램 관계가 되는 부분문자열의 총 개수를 출력해야 합니다.

예를 들어 S = “bacaAacba”, T = “abc”가 주어졌을 경우,
{bac}, {acb}, {cba} 총 3개의 부분문자열이 아나그램에 해당하므로 정답은 3이 됩니다.

해설

코딩테스트 JS 해시 풀이: 모든 아나그램 찾기 후반부 흐름 정리

처음 이 문제를 접했을 때, 직접적인 풀이를 떠올리지 못하고 접근 방법에서 막혔습니다.
문자열을 하나하나 잘라 비교하는 방식은 떠올릴 수 있었지만, 시간 복잡도 측면에서 적절한지 판단하기 어려웠습니다.

만약 S의 모든 부분 문자열을 생성해 정렬 후 비교한다면, 문자열 길이가 커질수록 연산량이 급격히 증가합니다.
따라서 이 문제는 단순 구현 문제가 아니라, 자료구조와 알고리즘 선택이 핵심인 문제입니다.

이 지점에서 해시(Map)를 사용해 문자 빈도를 관리하고, 슬라이딩 윈도우를 통해 연속된 구간을 효율적으로 이동시키는 접근이 필요해집니다.

풀이

먼저 문제의 전체 해답 코드를 한 번에 확인한 뒤, 아래에서 단계별 코드와 함께 흐름을 나누어 설명합니다.
한 줄씩 따라가며 이해할 수 있도록 실제 코딩테스트 풀이 순서에 맞게 구성했습니다.

function compareMaps(map1, map2) {
  if (map1.size !== map2.size) return false;
  for (let [key, val] of map1) {
    if (!map2.has(key) || map2.get(key) !== val) return false;
  }
  return true;
}

function solution(s, t) {
  let answer = 0;
  let tH = new Map();
  let sH = new Map();

  for (let x of t) {
    tH.set(x, (tH.get(x) || 0) + 1);
  }

  let len = t.length - 1;
  for (let i = 0; i < len; i++) {
    sH.set(s[i], (sH.get(s[i]) || 0) + 1);
  }

  let lt = 0;
  for (let rt = len; rt < s.length; rt++) {
    sH.set(s[rt], (sH.get(s[rt]) || 0) + 1);

    if (compareMaps(sH, tH)) answer++;

    sH.set(s[lt], sH.get(s[lt]) - 1);
    if (sH.get(s[lt]) === 0) sH.delete(s[lt]);
    lt++;
  }

  return answer;
}

1단계는 기준이 되는 문자열 T의 문자 빈도를 해시(Map)로 만드는 과정입니다.
이후 모든 비교는 이 해시를 기준으로 이루어집니다.

let tH = new Map();
for (let x of t) {
  tH.set(x, (tH.get(x) || 0) + 1);
}

아나그램 판별은 문자 종류와 개수가 완전히 같아야 하므로,
문자열 자체가 아니라 빈도 구조를 저장하는 것이 핵심입니다.

2단계는 슬라이딩 윈도우를 시작하기 전, S 문자열에서 미리 윈도우 크기만큼 해시를 구성하는 과정입니다.
윈도우 크기는 항상 T의 길이와 같아야 합니다.

let sH = new Map();
let len = t.length - 1;
for (let i = 0; i < len; i++) {
  sH.set(s[i], (sH.get(s[i]) || 0) + 1);
}

이렇게 하면 이후 오른쪽 포인터가 이동할 때마다,
정확히 길이가 T인 구간을 유지할 수 있습니다.

3단계부터 실제 슬라이딩 윈도우가 시작됩니다.
오른쪽 포인터를 이동시키며 새로운 문자를 해시에 추가합니다.

let lt = 0;
for (let rt = len; rt < s.length; rt++) {
  sH.set(s[rt], (sH.get(s[rt]) || 0) + 1);

  if (compareMaps(sH, tH)) answer++;

  sH.set(s[lt], sH.get(s[lt]) - 1);
  if (sH.get(s[lt]) === 0) sH.delete(s[lt]);
  lt++;
}

이 구간에서 가장 중요한 포인트는 두 가지입니다.
첫째, 해시 비교는 항상 윈도우 크기가 동일할 때만 수행됩니다.
둘째, 왼쪽 포인터에서 빠지는 문자는 반드시 정리해 주어야 합니다.

특히 빈도가 0이 된 문자를 삭제하지 않으면,
해시 크기 비교에서 오답이 발생할 수 있으므로 반드시 처리해야 합니다.

정리

이 문제는 아나그램이라는 개념보다, 연속된 부분 문자열을 어떻게 효율적으로 검사할 것인지가 핵심입니다.
해시와 슬라이딩 윈도우를 함께 사용하면 시간 복잡도를 크게 줄일 수 있으며,
문자열 관련 문제에서 매우 자주 활용되는 패턴입니다.

해시 빈도 집계 풀이를 더 짧게 점검하려면 JavaScript 해시 Map 학급 회장를 먼저 확인해보세요.

슬라이딩 윈도우 풀이 흐름을 더 연습하려면 JavaScript 최대 매출 슬라이딩 윈도우처럼 고정 길이 구간을 다루는 문제와 함께 보면 좋습니다.

QnA

Q. 왜 문자열을 정렬해서 비교하면 안 되나요?
정렬 방식은 구현은 쉽지만, 매번 정렬이 발생해 시간 복잡도가 크게 증가합니다. 이 문제의 입력 크기에서는 비효율적입니다.

Q. Map 대신 객체를 사용해도 되나요?
가능하지만, 키의 개수 관리와 비교 측면에서는 Map이 더 명확하고 안전합니다.

Q. 이 패턴은 어디에 또 사용되나요?
부분 문자열, 연속 구간 합, 빈도 비교 문제 등에서 거의 동일한 구조로 반복 등장합니다.

Q. 면접에서 이 문제를 설명하라고 하면 어떻게 말해야 하나요?
해시로 문자 빈도를 관리하고, 슬라이딩 윈도우로 연속 구간을 유지하며 비교했다고 설명하면 됩니다.

Q. 시간 복잡도는 어떻게 되나요?
문자열 S를 한 번 순회하므로 O(N)이며, 해시 비교는 문자 종류 수만큼만 수행됩니다.

Q. 대소문자 조건은 왜 중요한가요?
문자 자체를 키로 사용하기 때문에 대소문자 구분 여부가 결과에 직접적인 영향을 줍니다.

과정 마무리 실습

이미 푼 문제 한 개를 해설 없이 다시 풀고 경계 입력 세 개를 추가하세요.

완료 기준: 예상 출력과 실제 출력을 비교하고 시간 복잡도 및 다른 풀이의 장단점을 설명합니다.

이어서 공부할 과정: 자료구조 첫 글

전체 학습 경로

풀이 전에 확인할 순서

  1. 입력값과 출력값을 한 문장으로 다시 적습니다.
  2. 반복할 대상과 비교·저장할 값을 정합니다.
  3. 필요한 자료구조와 시간복잡도를 예상합니다.
  4. 코드를 보기 전에 손으로 작은 예제를 계산합니다.
힌트: 코딩테스트 JS 해시 풀이: 모든 아나그램 찾기

정답 코드를 바로 따라 쓰기보다, 본문에서 값이 갱신되는 조건과 반복 범위를 먼저 찾으세요. 반복 한 번마다 반드시 유지되어야 하는 값이 무엇인지 적으면 풀이의 중심 변수를 고르기 쉽습니다.

테스트 확인

  • 가능한 가장 작은 입력
  • 같은 값이나 문자가 반복되는 입력
  • 정답이 처음 또는 마지막 위치에서 결정되는 입력
  • 입력 제한에 가까운 경우의 실행 시간

확인 결과: 본문의 예제뿐 아니라 위 경계 사례에서도 예상값과 실제 출력이 같아야 풀이가 완료됩니다.

이 글이 도움이 되었나요?

조회 중

코딩테스트 JavaScript 학습 순서

필수 49개 · 전체 49개

읽음 기록 관리

전체 과정 목차 (49개)
  1. 필수 길잡이 · 코딩테스트 JS 자료구조 로드맵: 배열, 해시, 스택, 투 포인터 순서
  2. 필수 학습 · 세 수 중 최솟값 JavaScript 조건문 풀이 정리
  3. 필수 학습 · 삼각형 판별하기 JavaScript 풀이
  4. 필수 학습 · 연필 개수 JavaScript 풀이
  5. 필수 학습 · 1부터 N까지 합 출력하기 JavaScript 풀이
  6. 필수 학습 · 최솟값 구하기 JavaScript 풀이|배열 순회와 비교 갱신 원리
  7. 필수 학습 · 홀수 JavaScript 풀이: 조건 판별과 결과 처리 정리
  8. 필수 학습 · 10부제 JavaScript 풀이: 끝자리 비교로 위반 차량 수 세기
  9. 필수 학습 · A를 #으로 JavaScript 풀이: 문자열 순회와 치환
  10. 필수 학습 · 문자 찾기 JavaScript 풀이: 문자열 순회로 개수 세기
  11. 필수 학습 · 대문자 찾기 JavaScript 풀이
  12. 필수 학습 · 대문자로 통일 JavaScript 풀이
  13. 필수 학습 · 대소문자 변환 JavaScript 풀이
  14. 필수 학습 · 일곱 난쟁이 JavaScript 풀이: 두 명을 제외하는 완전탐색
  15. 필수 학습 · 코딩테스트 JS Map 풀이: 학급 회장 득표수 세기
  16. 필수 학습 · 코딩테스트 JS 스택 풀이: 올바른 괄호 검증하기
  17. 필수 학습 · 코딩테스트 JS 스택 풀이: 괄호문자 제거하기
  18. 필수 학습 · 코딩테스트 JS 스택 풀이: 크레인 인형뽑기 처리법
  19. 필수 학습 · 코딩테스트 JS 스택 풀이: 후위식 연산 계산하기
  20. 필수 학습 · 코딩테스트 JS 스택 풀이: 쇠막대기 레이저 절단 개수 세기
  21. 필수 학습 · 코딩테스트 JS 투 포인터 풀이: 두 정렬 배열 합치기
  22. 필수 학습 · 코딩테스트 JS 투 포인터 풀이: 공통 원소 추출하기
  23. 필수 학습 · 코딩테스트 JS 슬라이딩 윈도우 풀이: 최대 매출 구간 합 계산하기
  24. 필수 학습 · JavaScript 투 포인터: 합이 M인 연속 부분수열 개수
  25. 필수 학습 · 코딩테스트 JS 해시 풀이: 모든 아나그램 찾기 현재 글
  26. 필수 학습 · 가장 긴 문자열 JavaScript 풀이
  27. 필수 학습 · 가운데 문자 출력 JavaScript 풀이
  28. 필수 학습 · 중복문자제거 JavaScript 풀이
  29. 필수 학습 · 코딩테스트 JS 고급: 최소 힙으로 다익스트라 최단 경로 구하기
  30. 필수 학습 · 코딩테스트 JS Union-Find: 연결 성분 수와 크기 구하기
  31. 필수 학습 · 코딩테스트 JS Trie: 접두사에 맞는 단어 수 세기
  32. 필수 학습 · 코딩테스트 JS Fenwick Tree: 값 갱신과 구간 합 처리
  33. 필수 학습 · 코딩테스트 JS 세그먼트 트리: 단일 대입과 구간 합
  34. 필수 학습 · 코딩테스트 JS LRU 캐시: 지도 타일 재사용 기록
  35. 필수 학습 · 코딩테스트 JS AVL 트리: 기준 이상 최솟값 찾기
  36. 필수 학습 · 코딩테스트 JS 큐: 상담 창구 대기열 명령 처리
  37. 필수 학습 · 코딩테스트 JS 연결 리스트: 재생 대기 목록 관리
  38. 필수 학습 · JavaScript 원형 덱 연습: 최근 기록 창과 되돌리기
  39. 필수 학습 · JavaScript 해시 테이블 연습: 정규화 문자열 빈도와 등장 순서
  40. 필수 학습 · JavaScript 트리 순회 연습: 깊이별 노드 묶기
  41. 필수 학습 · JavaScript BST 연습: 닫힌 구간의 중복 키 보고서
  42. 필수 학습 · JavaScript 최소 힙 연습: 동률 순서를 지키는 작업 스케줄러
  43. 필수 학습 · JavaScript 그래프 연습: 연결 구역 크기를 작은 순서로 출력하기
  44. 필수 학습 · 중복단어제거 JavaScript 풀이
  45. 필수 학습 · TypeScript 이진 탐색 연습: 숫자 카드 존재 여부 확인
  46. 필수 학습 · 큰 수 출력하기 JavaScript 풀이
  47. 필수 학습 · 보이는 학생 JavaScript 풀이
  48. 필수 학습 · 가위바위보 JavaScript 풀이
  49. 필수 학습 · 점수계산 JavaScript 풀이

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기