궤도
[EPPER] 10회 8번 본문
문제
풀이
문제의 길이만큼이나 까다로웠다. 규칙은 간단하다.
1. 첫글자가 단축키가 될 수 있는지 확인
2. 두 개의 단어로 이루어졌다면 두번째 단어의 첫글자가 단축키가 될 수 있는지 확인
3. 위 과정에서 단축키를 얻지 못했다면 나머지 글자를 앞에서부터 탐색하며 단축키가 될 수 있는지 확인
그러니 조건문도 이 순서로 작성해야 한다.
우선 입력부터 받아보자. menu 구조체를 사용했는데 여기에는 각 메뉴(이름)와, 단축키의 위치, 그리고 해당 메뉴가 2개 이상의 단어로 이루어졌는지 체크하는 변수들이 존재한다.
단축키의 위치를 나타내는 key_pos는 1000으로 초기화 한다. 1000개 이상의 글자로 이루어진 메뉴가 들어올 일은 없겠지? 공백을 포함해서 입력을 받아야 하니 gets를 사용한다. 그리고 들어온 메뉴에 대해 공백이 있는지 확인하는 isSpace 함수를 호출한다. 이렇게 구조체에 모든 값을 채워넣을 수 있다.
이제 조건문을 돌며 단축키를 찾아볼 차례이다. 이 기능에서 핵심적인 함수는 isKey 함수이다. 이 함수는 특정문자에 대해 이 문자가 단축키가 될 수 있는지 확인한다. 모든 단축키를 저장한 key 배열을 전역변수로 선언했으니 확인하는 과정은 어렵지 않다.
조건문 안에는 bool 변수인 check가 있는데, 이건 3번 조건에서 사용할 것이다. 왜인지는 그때가서 설명하고 일단 1번 조건부터 보자.
메뉴의 첫번째 문자에 대해 단축키가 될 수 있는지 확인한다. 될 수 있다면 key 배열에 넣어주고 key_pos를 0으로 갱신한다. key를 찾았으니, check는 true가 된다.
만약 여기서 key를 찾지 못했다면 메뉴에 공백이 있는지 확인한다. 그럼 공백 다음 문자에 대해 아까와 같은 과정으로 key 여부를 확인한다. 여기서도 key를 찾지 못했다면 이제 3번 조건으로 가야할 차례이다.
왜 1번 조건과 2번 조건은 if와 else if로 하고 3번은 else가 아닌 if를 썼는지 의문을 가질 수도 있다. 하지만 3번 조건에서 단순하게 else를 사용한다면, 메뉴에 공백이 있으나 2번 조건에서 key를 찾지 못한 경우 3번 조건을 체크하지 못하고 그대로 조건문을 빠져나가게 된다. 그래서 check 변수를 뒀고 1번, 2번 조건에서 key를 찾지 못한 메뉴들을 마지막으로 체크하는 것이다.
한 문자에 대해 한 번만 함수를 호출했던 1번, 2번 조건과 달리 3번 조건은 앞에서부터 key여부를 계속 확인하니 key를 찾은 즉시 break를 통해 반복문을 빠져나가야 한다.
이제 마지막으로 출력을 하면 끝이다. key를 찾을 때마다 key_pos를 저장했으니 해당 위치의 문자를 출력할 때가 오면 좌우에 괄호를 씌워주면 된다. 출력은 그나마 쉬운 편이다.
소스코드
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
char key[31];
int key_cnt = 0;
typedef struct menu { //각 단어, key의 위치, 공백 여부를 구조체에 저장
char name[21];
int key_pos;
bool space;
}menu;
bool isSpace(char c[21]) { //공백이 있는지 확인
for (int i = 0; i < strlen(c); i++) {
if (c[i] == ' ')
return true;
}
return false;
}
bool isKey(char c) { //해당 문자가 key가 될 수 있는지 확인
for (int i = 0; i < key_cnt; i++) {
if (c == key[i])
return false;
}
return true;
}
int main() {
menu menus[31];
char empty;
int n;
bool check;
scanf("%d", &n);
for (int i = 0; i < n; i++) //key_pos 초기화
menus[i].key_pos = 1000;
scanf("%c", &empty);
for (int i = 0; i < n; i++) {
gets(menus[i].name); //공백 포함 입력
if (isSpace(menus[i].name)) //공백 여부 확인
menus[i].space = true;
else
menus[i].space = false;
}
for (int i = 0; i < n; i++) {
check = false;
if (isKey(menus[i].name[0])) { //첫번째 문자가 key가 될 수 있는지 확인
key[key_cnt++] = menus[i].name[0];
menus[i].key_pos = 0;
check = true;
}
else if (menus[i].space) { //공백이 있다면
for (int j = 0; j < strlen(menus[i].name); j++) {
if (menus[i].name[j] == ' ' && isKey(menus[i].name[j + 1])) { //공백 다음 문자가 key가 될 수 있는지 확인
key[key_cnt++]=menus[i].name[j+1];
menus[i].key_pos = j + 1;
check = true;
}
}
}
if (!check) { //위 두 과정에서 key를 찾지 못했다면
for (int j = 0; j < strlen(menus[i].name); j++) {
if (menus[i].name[j] != ' ' && isKey(menus[i].name[j])) { //공백이 아닌 문자에 대해 key가 될 수 있는지 확인
key[key_cnt++] = menus[i].name[j];
menus[i].key_pos = j;
break; //찾은 즉시 break
}
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < strlen(menus[i].name); j++) { //key에 해당하는 문자면 좌우에 괄호를 씌워줌
if (j == menus[i].key_pos)
printf("[");
printf("%c", menus[i].name[j]);
if (j== menus[i].key_pos)
printf("]");
}
printf("\n");
}
}