[알고리즘] 프로그래머스 불량 사용자 파이썬 풀이
문제 링크
https://programmers.co.kr/learn/courses/30/lessons/64064
풀이 방법
먼저 banned_id를 기준으로 user_id를 하나씩 돌면서
- 서로 글자수가 같고,
*
를 제외한 모든 글자가 서로 똑같은 user_id를 찾는다.
그걸 candidates
리스트로 넘겨준다. 그러면 각 banned_id가 가질 수 있는 후보 리스트가 완성이 된다.
이제 candidates
의 각 리스트를 돌면서 dfs
로 모든 경우의 수를 따져주면 된다.
dfs
의 파라미터로
앞으로 탐색해야할 banned_id에 해당하는 user_id 리스트 candidates
,
선택하지 않은 user_id 리스트 users
,
이번 경우의 수가 담긴 result,
최종 결과가 들어갈 세트 results
가 들어간다.
중간에 기존에 있던 레퍼런스들이 아닌 deepcopy
를 이용해 기존 내용이 똑같이 담긴 새로운 리스트들이 생성되는데
그렇게 하지 않으면 재귀를 이용한 백트랙킹이 안되기 때문이다.
이해가 되지 않는다면 예를 들어보자.
입력값이 user_id: ["frodo", "fradi", "crodo", "abc123", "frodoc"], banned_id: ["*rodo", "*rodo", "******"]
로 주어졌을 때 (입출력 예시2번)
candidates는 [['frodo', 'crodo'], ['frodo', 'crodo'], ['abc123', 'frodoc']]
로 나오게 된다.
['frodo', 'crodo']
이 리스트가 2번 들어간 이유는 banned_id
에 "*rodo"
이 조건이 2번 들어가있기 때문에 중복처리를 해주지 않고 들어가 있기 때문이다.
이제 dfs
함수로 들어가 ['frodo', 'crodo']
이 아이템부터 경우의 수를 따져본다.
여기서 frodo
를 선택한다면 다음 아이템에선 frodo
또는 crodo
,
다음 아이템에선 abc123
또는 frodoc
을 선택할 수 있다.
반대로 여기서 crodo
를 선택한다면 똑같이 다음 아이템에선 frodo
또는 crodo
,
다음 아이템에선 abc123
또는 frodoc
을 선택할 수 있다.
결국 results값으로
['frodo', 'crodo', 'abc123']
['crodo', 'frodo', 'abc123']
['frodo', 'crodo', 'frodoc']
['frodo', 'crodo', 'frodoc']
이 4 가지 경우가 나올 수 있다.
하지만 문제 조건에 나와있듯이 원소의 순서에 상관없이 리스트에 담긴 원소가 같다면 동일한 경우로 생각하기 때문에
중복된 부분을 제거해주어야 한다.
세트
를 통해서 중복을 없앨 수 있고 그러기 위해선 중복된 케이스가 같은 원소여야 한다.
하지만
['frodo', 'crodo', 'abc123']
['crodo', 'frodo', 'abc123']
이 케이스를 세트
에 넣어준다면 다른 원소로 인식이 될 것이다. (애초에 리스트
는 세트
에 삽입하지 못 하지만..)
같은 원소로 인식하기 위해서 dfs
의 base case로 해당 스택까지 트랙킹해온 result배열을 정렬해 준다.
그리고 리스트
를 튜플
에 넣는다면 hashable하지 않기 때문에 리스트
를 튜플
로 바꿔준다. (원소의 순서 고정)
1 | ['frodo', 'crodo', 'abc123'] => {'crodo', 'frodo', 'abc123'}로 변한됨 |
마지막으로 세트
로 선언된 results
에 넣어주면
['frodo', 'crodo', 'abc123']
['crodo', 'frodo', 'abc123']
와 같은 케이스는 중복처리 되어 하나만 남게된다.
최종적으로 results
가 가지고 있는 각 경우의 집합?의 갯수를 세어주면 된다.
코드
1 | import copy |
배운 점
파이썬의 함수 인자 전달 방식은 call-by-object-reference
이다.
mutable한 타입은 call-by-value
로,
immutable한 타입은 call-by-reference
로 함수의 인자가 전달된다.
나는 다른 언어를 쓰던 습관이 남아있어 call-by-value
인 줄 알고
dfs를 할 때 인자로 넘겨준 값이 예상한 값과 다른 값이 나왔다.
copy
모듈을 이용해 deepcopy
를 해서 레퍼런스가 아닌 새로운 객체가 넘겨지도록 바꾸어주었다.
++ 이러한 방식이 백트랙킹
임을 깨닫게 되었다!!!
++ 세트
에 리스트
를 넣을때는 튜플
로 변환해서 넣어줄 수 있다는 것을 알았다.