Jewels and Stones
두 개의 문자열을 받습니다. jewels의 각 문자는 보석의 한 종류를 나타내며, 문자는 반복되지 않습니다. stones의 각 문자는 당신이 가진 돌 하나를 나타냅니다. 당신의 돌 중 보석인 것이 몇 개인지 반환하세요. 문자는 대소문자를 구분합니다. "a"와 "A"는 서로 다른 종류입니다.
함수
- jewelsstring
- 보석으로 간주되는 돌의 종류, 각 한 글자
- stonesstring
- 네가 가진 돌, 돌마다 한 글자씩
- 반환값integer
- 문자가 보석에 나타나는 돌의 개수
제약 조건
1 ≤ jewels.length ≤ 521 ≤ stones.length ≤ 104- 두 문자열은 모두 영문자(소문자와 대문자)만 포함합니다.
- 글자열
jewels의 모든 글자는 서로 다릅니다.
예제
- 입력
- jewels = "rR"stones = "rubyRRr"
- 출력
- 4
- 설명
- 보석의 종류는
r과R입니다.rubyRRr에서r,R,R,r보석은 일치하지만u,b,y는 일치하지 않으므로 답은4입니다.
- 입력
- jewels = "z"stones = "ZZZ"
- 출력
- 0
- 설명
- 유일한 보석 종류는 소문자
z입니다. 모든 돌은 대문자Z로, 다른 종류이므로 어느 것도 해당되지 않습니다.
제출 시 숨은 테스트 +12개
힌트
하나씩 열어 보세요. 열 때마다 조금 더 알려 줍니다.
돌 하나의 경우, 그것이 해당되는지를 결정하는 질문은 무엇인가요?
각 돌마다 "이 문자가 보석인가요?"라고 묻습니다. 이 질문에 상수 시간으로 답하는 구조는 무엇인가요?
jewels의 각 문자를 집합에 넣은 다음,stones를 순회하며 집합에 포함된 문자를 모두 세세요. 대소문자는 그대로 유지하세요.
풀이
각 돌마다 하나의 답이 필요합니다. 이 문자가 보석인가요? 각 돌에 대해 jewels 문자열을 검색하면 같은 탐색을 계속 반복하게 됩니다. 보석 문자를 세트에 한 번 넣어 두면 각 돌은 한 번의 조회만으로 확인할 수 있습니다.
모든 돌을 보석으로 스캔하세요
핵심 아이디어
돌을 한 번에 하나씩 살펴보세요. 각 돌마다 jewels를 훑으며 돌과 같은 첫 번째 문자를 찾으면 멈춥니다. 일치하면 개수에 1을 더합니다. 첫 번째 예시에서 돌 u는 r 및 R과 비교되지만 일치하는 문자가 없어 아무것도 더하지 않습니다.
보석 문자는 모두 서로 다르므로 돌 하나는 많아야 보석 문자 하나와 일치할 수 있습니다. 따라서 처음 일치하는 문자를 찾으면 멈춰도 됩니다. 보석이 아닌 돌은 그렇다는 것을 알기 전에 모든 보석 문자와 비교해야 합니다.
보석 종류가 j개이고 돌이 s개라면 비교 횟수는 최대 j × s회입니다. 여기서 j ≤ 52이므로 돌이 10^4개여도 비교 횟수는 약 5 × 10^5회이며, 탐색은 시간 내에 끝납니다. 종류 목록이 늘어나면 비효율이 드러납니다. 돌마다 같은 검색을 다시 수행하기 때문입니다.
알고리즘
count를0으로 설정합니다.- 각 돌을
jewels의 각 문자와 비교합니다. - 처음으로 일치하는 문자를 찾으면
count에1을 더하고 다음 돌로 넘어갑니다. count를 반환합니다.
def numJewelsInStones(jewels, stones):
count = 0
for stone in stones:
for jewel in jewels:
if stone == jewel:
count += 1
break # the kinds are distinct: no second match is possible
return count보석을 집합에 넣으세요
핵심 아이디어
"이 문자가 보석인가?"라는 질문은 같은 문자에 대해 물을 때마다 항상 같은 답이 나옵니다. 그러니 종류별로 한 번만 답하면 됩니다. jewels의 문자로 집합을 만드세요. 집합은 상수 시간에 포함 여부를 알려 주므로, 각 보석을 확인할 때 훑어보는 대신 조회 한 번이면 됩니다.
첫 번째 예제에서 집합은 {r, R}입니다. rubyRRr를 순회하면 조회 결과는 예, 아니요, 아니요, 아니요, 예, 예, 예입니다. 보석은 네 개입니다. 집합을 만드는 데 j단계가 걸리고 순회에는 s단계가 걸리므로, 총 시간은 O(j + s)입니다.
집합에는 최대 52개의 문자가 들어갑니다. 내장 집합이 없는 언어에서는 문자 코드로 인덱싱한 플래그 배열로 같은 작업을 할 수 있습니다.
알고리즘
jewels의 모든 문자를 포함하는 집합을 만듭니다.count를0으로 설정합니다.- 각 돌에 대해 집합에 해당 문자가 포함되어 있으면
count에1을 더합니다. count를 반환합니다.
def numJewelsInStones(jewels, stones):
kinds = set(jewels)
count = 0
for stone in stones:
if stone in kinds:
count += 1
return count
함정과 경계 사례
알고리즘은 반복문 하나로 이루어져 있습니다. 오답은 문자를 비교하고 세는 방식에서 발생합니다.
- 대소문자를 무시하는 경우. 두 문자열을 모두 소문자로 바꾸면
z와Z가 일치하게 되어, 두 번째 예제의 결과가0이 아니라3이 됩니다. - 보석의 종류가 아니라 보석에 해당하는 돌의 개수를 세는 경우.
rubyRRr에는 보석 종류가 두 가지 있지만 보석에 해당하는 돌은 네 개입니다. 반복되는 돌도 포함해 모든 돌을 세야 합니다. - 돌을 순회하는 반복문 안에서 집합을 만드는 경우. 돌마다 집합을 다시 만들면 매번
j단계가 소요되어 순회 방식의O(j × s)로 되돌아갑니다. 반복문 전에 한 번만 만드세요. - 인수를 서로 바꾸는 경우. 집합에는
jewels가 들어 있어야 하고, 반복문은stones를 순회해야 합니다. 역할을 반대로 하면 두 번째 예제에서는 보석 종류 하나인z를 돌과 비교해 여전히0이 나오지만,("a", "aaa")의 결과는3이 아니라1이 됩니다.
자주 묻는 질문3
Jewels and Stones의 시간 복잡도는 얼마인가요?
집합을 사용하면 O(j + s)입니다. jewels에서 집합을 만드는 데 j단계가 걸리고, s개의 돌 각각에 대해 상수 시간 조회를 한 번씩 수행합니다. 각 돌마다 jewels를 검색하면 O(j × s)입니다.
Jewels and Stones에 해시 집합을 사용하는 이유는 무엇인가요?
각 돌은 그 문자가 보석인지 같은 종류의 질문을 합니다. 해시 집합은 이를 상수 시간에 답하지만, jewels 문자열을 검색하는 데는 문자열 길이에 비례하는 시간이 걸립니다. 집합을 만드는 데 한 번 비용을 들이면 그 이후에는 돌을 확인할 때마다 시간을 절약할 수 있습니다.
집합 없이 풀 수 있나요?
네. 문자는 영어 문자이므로 문자 코드로 인덱싱하는 128개 또는 256개의 플래그 배열을 해싱 없이 집합처럼 사용할 수 있습니다. 각 보석 문자를 표시한 다음, 플래그가 설정된 돌의 개수를 셉니다. Ruby의 stones.count(jewels)는 한 번의 호출로 모든 작업을 처리하지만, 플래그 배열은 내부에서 어떤 일이 일어나는지 보여줍니다.
비슷한 문제
같은 아이디어를 쓰는 문제입니다. 두세 개를 풀면 패턴이 몸에 익습니다.
Python
def numJewelsInStones(jewels, stones):
# 여기에 코드를 작성하세요케이스 1
케이스 2
입력
jewels = "rR" stones = "rubyRRr"
기대값
4