N Log

프로그래머스 입문 120853 컨트롤 제트

문제

출처

숫자와 "Z"가 공백으로 구분된 문자열 s가 매개변수로 주어진다.
숫자가 나오면 합계에 더하고, "Z"가 나오면 합계에서 직전 숫자를 빼면 된다.

s result
“1 2 Z 3” 4
“10 20 30 40” 100
“10 Z 20 Z 1” 1
“10 Z 20 Z” 0
“-1 -2 -3 Z” -3

풀이

직전 정수값 추적으로 취소 처리하기

#include <stdlib.h>
#include <string.h>

int solution(const char* s)
{
    int prev_num;
    int sum = 0;
    char buffer[256];

    strcpy(buffer, s);

    char* token = strtok(buffer, " ");

    while (token != NULL)
    {
        if (strcmp(token, "Z") == 0)
        {
            sum -= prev_num;
        }
        else
        {
            int num = atoi(token);
            sum += num;
            prev_num = num;
        }

        token = strtok(NULL, " ");
    }

    return sum;
}

strtok로 공백으로 구분된 토큰을 하나씩 가져온다.
strcmp는 문자열을 비교하는 함수이므로, Z가 한 글자여도 문자열 리터럴인 "Z"를 인자로 넘긴다.
토큰이 "Z"가 아니라면 정수로 변환한 뒤 sum에 더한다.
이때 이후 "Z"를 만났을 때 직전 덧셈을 취소할 수 있도록, 더한 값을 prev_num에 저장해 둔다.
토큰이 "Z"라면 sum에서 prev_num을 빼서 직전 덧셈을 취소한다.

취소 값을 배열에 저장해 합계 구하기

#include <stdlib.h>
#include <string.h>

int solution(const char* s)
{
    int count = 0;
    int nums[256];
    int sum = 0;
    char buffer[256];

    strcpy(buffer, s);

    char* token = strtok(buffer, " ");

    while (token != NULL)
    {
        if (strcmp(token, "Z") == 0)
        {
            nums[count] = nums[count - 1] * -1;
        }
        else
        {
            nums[count] = atoi(token);
        }

        count++;
        token = strtok(NULL, " ");
    }

    for (int i = 0; i < count; i++)
    {
        sum += nums[i];
    }

    return sum;
}

앞선 방식은 토큰 판별과 변환, 계산을 하나의 반복문에서 모두 처리했다.
이번 방식은 문자열을 int 배열로 바꾼 뒤 합계를 구한다.
숫자 토큰은 int 값으로 변환해 배열에 추가하고, "Z"를 만나면 배열에 저장된 직전 값의 부호를 바꾼 값을 새 원소로 추가한다.
이렇게 하면 "Z"가 직전 값을 빼야 한다는 의미를 배열 안에 기록할 수 있다.
배열이 완성되면 모든 값을 더해 최종 합계를 구한다.

strtok 없이 구현하기

#include <stdlib.h>

int get_token_length(const char* current)
{
    int length = 0;

    if (current[length] == '-')
    {
        length++;
    }

    while (current[length] >= '0' && current[length] <= '9')
    {
        length++;
    }

    return length;
}

int solution(const char* s)
{
    int sum = 0;
    int prev_num = 0;
    const char* current = s;

    while (*current != '\0')
    {
        if (*current == 'Z')
        {
            sum -= prev_num;
            current++;
        }
        else
        {
            int num = atoi(current);

            sum += num;
            prev_num = num;
            current += get_token_length(current);
        }

        if (*current == ' ')
        {
            current++;
        }
    }

    return sum;
}

strtok를 사용할 수 없다면 atoi가 문자열 앞쪽의 유효한 정수 부분만 변환한다는 점을 이용할 수 있다.
예를 들어 atoi("-123abc")atoi("-123 abc")는 정수 -123을 반환한다.
따라서 현재 포인터 위치에서 atoi를 호출하면 별도로 토큰을 잘라내지 않아도 정수값을 얻을 수 있다.
이후 다음 토큰을 가리키도록 포인터를 이동시킨다.