링크 : https://www.acmicpc.net/problem/1062
문제 설명 :
남극에 사는 김지민 선생님은 학생들이 되도록이면 많은 단어를 읽을 수 있도록 하려고 한다. 그러나 지구온난화로 인해 얼음이 녹아서 곧 학교가 무너지기 때문에, 김지민은 K개의 글자를 가르칠 시간 밖에 없다. 김지민이 가르치고 난 후에는, 학생들은 그 K개의 글자로만 이루어진 단어만을 읽을 수 있다. 김지민은 어떤 K개의 글자를 가르쳐야 학생들이 읽을 수 있는 단어의 개수가 최대가 되는지 고민에 빠졌다.
남극언어의 모든 단어는 "anta"로 시작되고, "tica"로 끝난다. 남극언어에 단어는 N개 밖에 없다고 가정한다. 학생들이 읽을 수 있는 단어의 최댓값을 구하는 프로그램을 작성하시오.
입력 :
첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문자로만 이루어져 있고, 길이가 8보다 크거나 같고, 15보다 작거나 같다. 모든 단어는 중복되지 않는다.
출력 :
첫째 줄에 김지민이 K개의 글자를 가르칠 때, 학생들이 읽을 수 있는 단어 개수의 최댓값을 출력한다.
예제 입력 :
3 6 antarctica antahellotica antacartica
예제 출력 :
2
접근법 :
1) 어떻게 풀 것인가?
N과 K가 모두 매우 작은 편이다. (50이하) -> 완전탐색이 가능할 확률이 높다.
K개의 알파벳 조합으로 단어를 구성할 수 있는지를 묻는 문제이기에 "조합"을 구현할 수 있어야하고
완전탐색 + 백트래킹 방식으로 접근이 가능하다. 개인적으로 조합 문제는 DFS를 선호.
기본적인 조합 DFS에 마지막에 개수를 체크하는 함수만 구현한다면 답을 구할 수 있다.
참조 : DFS (깊이 우선 탐색)
https://ko.wikipedia.org/wiki/%EA%B9%8A%EC%9D%B4_%EC%9A%B0%EC%84%A0_%ED%83%90%EC%83%89
2) 시간복잡도
K 최대 26으로 가능한 최대 조합 숫자는 26C13 (26 Combination 13) → 약 1,000만
단어의 개수 50개로 최악의 경우에도 큰 무리없을 것으로 예상
(Java 실행시간 300ms)
3) 공간복잡도
알파벳 조합 26개는 boolean, 단어 조합 역시 50개의 String (최대 길이 15)이므로
굳이 계산할 필요가 없을정도로 여유있음.
4) 풀면서 놓쳤던점
- C++을 오랜만에 해서 string을 scanf로 입력받으려고 함
→ char [ ] 입력은 scanf, string 입력은 cin
- DFS 진행시 가능한 K를 모두 사용하고 개수를 체크하는 로직 전에
id의 범위를 넘어가면 return 시키는 로직을 위치시켜서 오답이 발생함.
5) 이 문제를 통해 얻어갈 것
삼성 SW역량테스트 A형(Advanced) 2문제 중 쉬운 문제보다 조금 더 쉬운 난이도로 예상.
조합, DFS, 백트래킹을 연습할 수 있으며, 함수를 나누는 연습을 하기에도 적절함.
기회가 된다면 alphabet 체크하는 상황에서 비트 마스킹을 통해서도 가능할 것 같다.
삼성 SW역량테스트 C형은 bit masking, little endian, big endian 등 computer science 적인 내용을 기본적으로
알아야 문제 해결에 도움이 되기에 다음에 bit masking 방식으로 풀어봐야겠다.
C++ 코드 :
// 가르침 1062 C++
#if 1
#pragma warning(disable:4996)
#include <cstdio>
#include <vector>
#include <string>
using namespace std;
int n, k; // n : 단어개수, k : 배울 글자 수
vector <string> words; // 단어 배열
bool alphabet[26]; // 알파벳 배우는 여부 체크
int ans; // 출력할 답
void input();
void makeSol();
void dfs(int id, int cnt);
int countWords();
int main() {
// 1. 입력을 받는다
// freopen("input.txt", "r", stdin);
input();
// 2. 정답을 찾는다.
makeSol();
// 3. 답을 출력한다.
printf("%d", ans);
return 0;
}
void input() {
scanf("%d %d", &n, &k);
// cin >> n >> k;
char inputString[16];
//string inputString; --> cin >> inputString
for (int i = 0; i < n; i++) {
scanf("%s", inputString);
words.push_back(inputString);
}
}
void makeSol() {
// * 예외조건 - k<5이면 정답 : 0
if (k < 5) {
return;
}
// a, c, i, n, t
alphabet['a' - 'a'] = true;
alphabet['c' - 'a'] = true;
alphabet['i' - 'a'] = true;
alphabet['n' - 'a'] = true;
alphabet['t' - 'a'] = true;
dfs(1, k - 5);
}
void dfs(int id, int cnt) {
// * 탈출조건 - 가능한 알파벳을 모두 사용한 경우, 정답 개수 체크
if (cnt == 0) {
int result = countWords();
ans = result > ans ? result : ans;
return;
}
// id가 알파벳 개수 26개를 넘어갈 경우 종료
if (id >= 26) return;
// 아직 사용하지 않은 알파벳일때만 dfs로 확인
if (alphabet[id] == false ) {
alphabet[id] = true;
dfs(id + 1, cnt - 1);
alphabet[id] = false;
}
// 현재 알파벳을 pass하고 dfs 진행
dfs(id + 1, cnt);
}
int countWords() {
string word; // 단어
bool flag; // 단어별 가능여부 체크 flag
int result = 0; // 현재 알파벳 조합으로 가능한 정답
for (int i = 0; i < n; i++) {
flag = true;
word = words[i];
int len = word.size();
for (int j = 0; j < len; j++) {
char checkAlphabet = word[j];
if (alphabet[checkAlphabet - 'a'] == false) {
flag = false;
break;
}
}
if (flag) result++;
}
return result;
}
#endif