문제
좋은 패스워드를 만드는것은 어려운 일이다. 대부분의 사용자들은 buddy처럼 발음하기 좋고 기억하기 쉬운 패스워드를 원하나, 이런 패스워드들은 보안의 문제가 발생한다. 어떤 사이트들은 xvtpzyo 같은 비밀번호를 무작위로 부여해 주기도 하지만, 사용자들은 이를 외우는데 어려움을 느끼고 심지어는 포스트잇에 적어 컴퓨터에 붙여놓는다. 가장 이상적인 해결법은 '발음이 가능한' 패스워드를 만드는 것으로 적당히 외우기 쉬우면서도 안전하게 계정을 지킬 수 있다.
회사 FnordCom은 그런 패스워드 생성기를 만들려고 계획중이다. 당신은 그 회사 품질 관리 부서의 직원으로 생성기를 테스트해보고 생성되는 패스워드의 품질을 평가하여야 한다. 높은 품질을 가진 비밀번호의 조건은 다음과 같다.
- 모음(a,e,i,o,u) 하나를 반드시 포함하여야 한다.
- 모음이 3개 혹은 자음이 3개 연속으로 오면 안 된다.
- 같은 글자가 연속적으로 두번 오면 안되나, ee 와 oo는 허용한다.
이 규칙은 완벽하지 않다;우리에게 친숙하거나 발음이 쉬운 단어 중에서도 품질이 낮게 평가되는 경우가 많이 있다.
입력은 여러개의 테스트 케이스로 이루어져 있다.
각 테스트 케이스는 한 줄로 이루어져 있으며, 각 줄에 테스트할 패스워드가 주어진다.
마지막 테스트 케이스는 end이며, 패스워드는 한글자 이상 20글자 이하의 문자열이다. 또한 패스워드는 대문자를 포함하지 않는다.
풀이
반복문을 사용해 한 번에 한 단어씩 받아 `.strip()`을 활용하여 공백, 줄바꿈 문자를 제거한 뒤, 세 가지 조건들을 순차적으로 반복문과 조건문을 사용하여 검사하도록 로직을 짰다. 이 때 break문, isFalse 같은 플래그를 활용하여 비밀번호에 적합하지 않다는 게 결정되자마자 검사가 조기 종료되도록 구현하여 최대한 효율성을 챙기려고 노력했다. 모음은 상단에 list로 선언해둔 뒤 조건 1,2에서 사용하였다.
첫 번째 코드
import sys
input = sys.stdin.readline
vowels = ["a", "e", "i", "o", "u"]
while True:
s = input().strip() # 양 끝의 공백, \n 제거
if s == "end":
break
if not any(word in s for word in vowels):
print(f"<{s}> is not acceptable.")
continue
v_cnt, c_cnt = 0, 0
isBreak = False
for c in s:
if c in vowels:
v_cnt += 1
c_cnt = 0
else:
v_cnt = 0
c_cnt += 1
if v_cnt == 3 or c_cnt == 3:
isBreak = True
break
if isBreak == True:
print(f"<{s}> is not acceptable.")
continue
for i in range(len(s)):
if i == len(s) - 1:
break
if s[i] == s[i + 1] and s[i] != "e" and s[i] != "o":
isBreak = True
break
if isBreak == True:
print(f"<{s}> is not acceptable.")
else:
print(f"<{s}> is acceptable.")
원하던 대로 동작은 하지만 지나치게 조기 종료에만 신경쓰다보니 최종 출력문과 플래그 변수가 여기저기 흩어져있어 가독성이 많이 떨어진다고 생각하게 되었다. 내가 구현한 개별 조건 검사 로직은 그대로 유지하되 전체적으로 봤을 때 가독성을 포함해 개선할 수 있는 사항은 개선해보기로 하였다.
1. 검증 로직을 함수로 분리
: 매번 break문 , Flag 변수를 검사하는 로직을 사용하는 대신 검증 로직 전체를 함수로 분리하여 False를 바로 리턴하고 검증 로직이 종료될 수 있도록 수정하였다.
2. vowels를 list 대신 set으로 선언
: vowels를 기존의 list 선언 대신 set 선언으로 변경하였다. 지금은 원소가 5개밖에 없어 차이가 크게 없어보여도 시간복잡도가 list는 O(n), set은 O(1)이라는 것을 알게 되어 앞으로는 set을 사용하는 것을 습관화하기로 하였다.
3. sys.stdin.read().splitlines()으로 한번에 입력 받기
: 기존의 .strip()을 사용해 매번 공백을 제거하던 방식 대신 한 번에 모든 입력을 받아 \n 기준으로 리스트로 만들고, 그 원소를 하나씩 꺼내와 검사하는 방식으로 수정하였다.
4. 조건문 범위 range(len(s) - 1)로 수정
: 사소하지만 세번째 조건 검사 로직에서 범위를 -1 함으로써 for문 안의 조건문 한 줄을 생략할 수 있게 되었다.
최종 코드
import sys
def check_password(s):
vowels = {"a", "e", "i", "o", "u"}
if not any(word in s for word in vowels):
return False
v_cnt, c_cnt = 0, 0
for c in s:
if c in vowels:
v_cnt += 1
c_cnt = 0
else:
v_cnt = 0
c_cnt += 1
if v_cnt == 3 or c_cnt == 3:
return False
for i in range(len(s) - 1):
if s[i] == s[i + 1] and s[i] != "e" and s[i] != "o":
return False
return True
input = sys.stdin.read().splitlines()
for s in input:
if s == "end":
break
if check_password(s):
print(f"<{s}> is acceptable.")
else:
print(f"<{s}> is not acceptable.")
가독성이 훨씬 좋아졌고 코드가 깔끔해졌다!

배운 것
- sys.stdin.read().splitlines(): 한 번에 모든 입력을 받아 \n 기준으로 리스트로 생성한다.
- any(word in s for word in vowels): (결과값 for 반복변수 in 반복가능객체) ... vowels에서 원소(word)를 하나씩 꺼내서 s 안에 있는지 검사하고 True 또는 False를 반환한다. 모든 원소의 결과값들 중 하나라도 True가 있다면 any(...)는 True.
너무어렵다ㅜㅜ - f-string: 문자열 맨 앞("" 또는 '')에 f를 붙인 뒤 문자열 안에 변수를 {}로 감싸서 넣어준다.
'[Python] 백준 알고리즘 공부' 카테고리의 다른 글
| [Python] 1205. 등수 구하기 (0) | 2026.02.20 |
|---|---|
| [Phython] 20125. 쿠키의 신체 측정 (0) | 2026.02.20 |
| [Phython] 25757. 임스와 함께하는 미니게임 (2) | 2026.02.18 |
| [Python] 7568. 덩치 (0) | 2026.02.16 |
| [Python] 10431. 줄세우기 (0) | 2026.01.19 |