문제
영어 대소문자와 공백으로 이루어진 문자열이 주어진다. 이 문자열에는 몇 개의 단어가 있을까? 이를 구하는 프로그램을 작성하시오. 단, 한 단어가 여러 번 등장하면 등장한 횟수만큼 모두 세어야 한다.
입력
첫 줄에 영어 대소문자와 공백으로 이루어진 문자열이 주어진다. 이 문자열의 길이는 1,000,000을 넘지 않는다. 단어는 공백 한 개로 구분되며, 공백이 연속해서 나오는 경우는 없다. 또한 문자열은 공백으로 시작하거나 끝날 수 있다.
출력
첫째 줄에 단어의 개수를 출력한다.
생각, 풀이 과정
문자열 길이가 1,000,000을 넘지 않는다고 하니 마지막에 널 문자 ('\0')가 들어갈 것까지 생각을 해서 char 배열은 1,000,001로 잡아준다.
문자열을 입력을 받고 공백이 나올 때마다 count 값을 올리는데, 첫 번째 원소와 마지막 원소에서 나오는 공백은 count 하지 않는다. (이 코드를 포인터 역참조를 이용해서 for문을 작성하였다.) (strlen()함수도 작성했으니 둘 다 보면서 비교하면 포인터 실력이 상승할지도?!)
그리고 문제에는 정확하게 없지만 공백만 하나 있는 문자열도 테스트 케이스에 있으니 공백하나만 있는 문자열을 판단하는 조건문도 하나 추가해서 넣어야겠다.
scanf("%[^\n]s", str) /* [^\n]의 의미는 개행 문자를 만날 때 까지 입력을 계속 받는다 */
scanf() 함수에서 '%[^\n]s' 것을 처음 보았는데, 어떻게 작동하는지를 보니 ' ^ '뒤에 있는 문자가 나올 때까지 계속해서 문자를 읽으라는 의미였다.
scanf()로 입력을 받으면 scanf("%[^\n]s", str); 와 같이 작성하지 않고 그냥 scanf("%s", str); 로 작성하면 공백문자 앞까지만 문자열을 입력받기 때문에 전자와 같은 코드를 작성해야 한다.
코드 (포인터 이용)
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif /* _CRT_SECURE_NO_WARNINGS */
#include <stdio.h>
#include <string.h>
enum {
MAX_STRING_LENGTH = 1000001 };
int main(void)
{
char str[MAX_STRING_LENGTH];
int i;
size_t count = 1;
/* fgets(str, MAX_STRING_LENGTH, stdin); */
scanf("%[^\n]s", str);
for (i = 0; *(str + i) != '\0'; ++i) {/* 문자열에서 널 문자 만날 때까지 이동하는 반복문 */
if ((i != 0) && (*(str + i) == ' ') && (*(str + i + 1) != '\0') ) {
++count; /* 맨 앞과 맨 뒤 공백문자는 제외하고 공백문자 카운드하는 조건문 */
} else if ( i == 0 && *(str + i) == ' ' && *(str + i + 1) == '\0') {
--count; /* 맨 앞에 공백문자가 있으면서 그 뒤에는 문자가 없는 문자열 판단하는 조건문 */
}
}
printf("%llu", count);
return 0;
}
*(str + i)는 str[i] 이랑 같은 의미다. 문자열은 널 문자까지 들어가므로 널 문자 만나면 반복문이 끝나게 만들어 놓았다. 이 코드에서는 count의 기본 값을 1로 맞춰 놓았다. 맨 앞에 공백이 없으면 문자 개수를 0으로 판단하기에 기본 값을 1로 잡아 놓고 맨 앞에 공백이 있으면 count--를 해버렸다.
코드 ( strlen() 함수 이용)
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif /* _CRT_SECURE_NO_WARNINGS */
#include <stdio.h>
#include <string.h>
enum {
MAX_STRING_LENGTH = 1000001 };
int main(void)
{
char str[MAX_STRING_LENGTH];
int i;
size_t count = 0;
size_t string_length;
scanf("%[^\n]s", str);
string_length = strlen(str);
for (i = 0; i < string_length; ++i) {
if (i == 0 && str[i] != ' ') /* 맨 앞에 공백이 없는 문자열 인지 확인 */
count++;
if (str[i] == ' ') /* 문자열에 공백이 있는지 확인 */
count++;
}
if (str[string_length - 1] == ' ') { /* 맨 끝에 공백이 있으면 count빼기 */
count--;
}
printf("%llu", count);
return 0;
}
여기서 count 값을 0으로 초기화했는데, count값이 0으로 잡더라도 맨 앞에 공백이 없는 문자열이면 count++해주고 맨앞에 공백이 있으면 알아서 count++해주니 문자 개수를 세어준다.
strlen() 함수를 사용하면 공백 하나만 있는 문자열을 고려하는 조건문을 만들 필요는 없다. 왜냐하면 [공백문자 하나를 카운트한다. -> 이후 맨 끝에 공백이 있으니 count를 뺀다.] 이런 식으로 작동하기 때문에 조건문을 따로 안 만들어도 알아서 작동이 된다.
반복문에 strlen() 함수 넣으면 안 됨!!
반복문안에 for (i = 0; i < strlen(str); ++i) 이렇게 넣으면 되겠지?라고 생각할 수 있다. 하지만 단점이 있다. 조건문을 판단할 때 strlen() 함수가 실행이 돼서 매번 계산을 하게 된다. 그래서 변수에 strlen(str)를 대입하고 난 뒤에 조건문에 넣으면 조건문 판단만 할 뿐 strlen() 함수를 한 번만 실행한다. 간단한 문제는 시간이 오래 안 걸릴 수 있지만 str배열이 엄청 길어진다면 시간이 점점 느려질 것이다.
추가적인 생각, 아쉬운 점
원래는 scanf()로 입력받으려면 scanf("%[^\n]s", str)와 같은 코드를 작성해야 돼서 fgets() 함수로 받고 싶었다.(그래서 주석에 들어가 있음)
그런데 fgets함수의 단점은 개행 문자인 '\n'도 문자열 안에 들어오기 때문에 포인터로 작성하는 반복문을 간결하게 작성하지 못하는 문제가 있다고 판단하여 사용하지 못했다.
그리고 앞, 뒤로 공백문자가 들어올 수 있다고 되어있는 조건이 있어서 앞, 뒥 공백문자도 신경 써야 되고 공백문자만 있는 문자열도 고려하면서 조건문을 작성하기에 조금 어려웠던 것 같다.
이렇게 조건문을 작성하는 것이 간결하게 한 건지 아닌지 확신이 들지 않는다.
만약에 이 코드를 보시고 아쉬운 점이 있으시다면 댓글로 달아주시면 감사하겠습니다.
'C언어 > BOJ' 카테고리의 다른 글
[C언어] 백준 2562번 최댓값 (0) | 2023.03.05 |
---|---|
[C언어] 백준 1546번 평균 (0) | 2023.02.25 |
[C언어] 백준 1157번 단어 공부 (0) | 2023.02.25 |
[C언어] 백준 1000 번 A+B (0) | 2023.02.20 |
댓글