걸린 시간을 1h30m처럼 적은 표기 n개가 한 줄에 하나씩 주어진다.
표기는 '덩이'를 빈칸 없이 하나 이상 이어 붙인 것이고, 덩이 하나는 '수' 다음에 '단위' 한 글자가 오는 꼴이다.
수는 숫자(0 ~ 9) 한 개 이상으로 적고, 앞에 0이 붙어 있어도 된다(007은 7이다).
단위는 d(날, 86400초), h(시간, 3600초), m(분, 60초), s(초) 네 가지다.
표기를 왼쪽부터 덩이 하나씩 읽으며 다음 규칙을 차례로 검사한다.
규칙 1: 덩이가 시작하는 자리의 글자는 숫자여야 한다. 아니면 그 자리가 틀린 자리다.
규칙 2: 수 다음에는 글자가 있어야 한다. 수에서 표기가 끝나면 틀린 자리는 (표기의 길이 + 1)이다.
규칙 3: 수 다음 글자는 d, h, m, s 가운데 하나여야 한다. 아니면 그 글자의 자리가 틀린 자리다.
규칙 4: 둘째 덩이부터는 단위가 바로 앞 덩이의 단위보다 작아야 한다(크기는 d > h > m > s). 같거나 크면 그 단위 글자의 자리가 틀린 자리다.
규칙 5: 둘째 덩이부터는 단위가 h이면 수가 23 이하, m이면 59 이하, s이면 59 이하여야 한다. 넘으면 그 수가 시작하는 자리가 틀린 자리다.
첫 덩이의 수는 얼마든 괜찮다(90m은 맞는 표기다).
한 덩이에서 규칙이 여럿 깨지면 번호가 작은 규칙의 틀린 자리를 쓰고, 틀린 덩이를 만나면 그 뒤는 읽지 않는다.
자리는 표기의 첫 글자를 1로 센다.
표기마다 한 줄씩, 맞는 표기이면 "ok T"(T는 모든 덩이의 수 × 단위의 초를 더한 값)를, 틀린 표기이면 "error p"(p는 틀린 자리)를 출력하라.
수를 읽는 함수가 '읽은 값'과 '수가 끝난 다음 자리'를 함께 돌려주고, 표기 하나를 검사하는 함수가 '맞는지'와 '초 또는 틀린 자리'를 함께 돌려주도록 만들어 보자(채점은 출력만 본다).
입력
첫 줄에 n(1 ≤ n ≤ 1,000), 다음 n줄에 표기가 하나씩 주어진다. 표기는 길이가 1 이상 40 이하이고 숫자와 영어 소문자로만 되어 있다. 숫자가 이어지는 길이는 9 이하다
출력
n줄. 줄마다 "ok T" 또는 "error p". T와 p는 쉼표 없이 숫자만 쓴다
예시 1 · stdin
6
1h30m
90m
1h75m
30000d
15
1m2h
stdout
ok 5400
ok 5400
error 3
ok 2592000000
error 3
error 4
예시 2 · stdin
5
1h2h
0d007h
h5
2d24h
1h30x
stdout
error 4
ok 25200
error 1
error 3
error 5
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
수를 읽고 나면 알아야 할 것이 둘이에요: 읽은 값과, 어디까지 읽었는지(다음 자리). 값 하나만 돌려주는 함수로는 둘 중 하나를 잃어서, 값 둘을 함께 돌려주는 방법이 언어마다 달라요. Python은 튜플 return value, i를 value, i = read_number(s, i)로 받아요. JavaScript는 배열 return [value, i]를 const [value, next] = readNumber(s, i)로 풀고, TypeScript는 돌려주는 타입을 튜플 [number, number]로 적어요. C#은 튜플 (long value, int next)를 돌려주고 (value, i) = ReadNumber(s, i)처럼 이미 있는 변수에 바로 풀어 넣을 수 있어요. C++은 std::pair<long long, int>를 auto [value, next] = readNumber(s, i);(C++17)로 받아요. Java는 값 하나만 돌려줘서 record Num(long value, int next) {}로 묶고 num.value()로 꺼내요. C도 값 하나만 돌려줘서, 값은 return으로 돌려주고 다음 자리는 포인터 int *next에 써 줘요. 크레이콘은 목록 돌려주기 [값, i];를 r[0], r[1]로 꺼내요. 표기 하나를 검사하는 함수도 (맞는지, 초 또는 틀린 자리) 둘을 같은 방법으로 돌려줘요. 함정 하나: 수를 읽은 뒤에는 자리가 이미 단위로 넘어가 있어요. 수가 시작한 자리를 따로 담아 두지 않으면 규칙 5에서 단위의 자리를 알리게 돼요. 예시 1의 1h75m은 75가 시작하는 3이 답인데 5가 나와요. 함정 둘: 한도는 둘째 덩이부터예요. 첫 덩이에도 걸면 예시 1의 90m이 error 1이 돼요. 함정 셋: 수 다음에 글자가 남았는지 먼저 봐야 해요. 예시 1의 15에서 그냥 s[i]를 읽으면 Python은 IndexError로 멈춰요(Java·C#도 예외로 멈추고, JavaScript는 undefined, C는 끝의 '\0'을 읽어요). 답은 길이 + 1인 error 3이에요. 함정 넷: 규칙 4는 '작아야'라서 같은 단위도 안 돼요. <=를 <로 쓰면 예시 2의 1h2h가 ok 10800이 돼요. 함정 다섯: 합은 999999999d23h59m59s에서 86,399,999,999,999초까지 커져요. 예시 1의 30000d만 해도 2,592,000,000초라 int(약 21억)를 넘어서, Java·C#의 int로 더하면 -1702967296이 찍혀요(C·C++에서는 정해지지 않은 동작이에요). Java·C#은 long, C·C++은 long long을 써요. Python의 정수는 크기 제한이 없고, JavaScript·TypeScript·크레이콘의 수는 2^53(약 9 × 10^15)까지 정확해서 그대로 맞아요.
Pythonmain.py · 50 줄
import sysUNITS = "dhms" # 큰 단위부터SECONDS = [86400, 3600, 60, 1]LIMIT = [0, 23, 59, 59] # 첫 덩이가 아닐 때 넘으면 안 되는 값(d는 늘 첫 덩이라 쓰이지 않는다)def read_number(s, i): """i번 자리부터 숫자를 읽어 (값, 숫자가 끝난 다음 자리)를 함께 돌려준다.""" value = 0 while i < len(s) and "0" <= s[i] <= "9": value = value * 10 + int(s[i]) i += 1 return value, idef parse(s): """(True, 모두 몇 초) 또는 (False, 처음 틀린 자리)를 돌려준다.""" total = 0 last = -1 # 바로 앞 덩이의 단위 번호 i = 0 while i < len(s): start = i value, i = read_number(s, i) if i == start: # 숫자가 와야 할 자리 return False, start + 1 if i == len(s): # 단위 없이 끝났다 return False, i + 1 u = UNITS.find(s[i]) if u < 0 or u <= last: # 단위가 아니거나, 앞 단위보다 작지 않다 return False, i + 1 if last >= 0 and value > LIMIT[u]: return False, start + 1 # 틀린 곳은 수가 시작한 자리 total += value * SECONDS[u] last = u i += 1 return True, totaldef main(): lines = sys.stdin.read().split() n = int(lines[0]) out = [] for s in lines[1:1 + n]: ok, x = parse(s) out.append(("ok " if ok else "error ") + str(x)) print("\n".join(out))main()
JavaScriptmain.js · 44 줄
const lines = require("fs").readFileSync(0, "utf8").trim().split(/\s+/);const UNITS = "dhms"; // 큰 단위부터const SECONDS = [86400, 3600, 60, 1];const LIMIT = [0, 23, 59, 59]; // 첫 덩이가 아닐 때 넘으면 안 되는 값// i번 자리부터 숫자를 읽어 [값, 숫자가 끝난 다음 자리]를 배열로 돌려준다function readNumber(s, i) { let value = 0; while (i < s.length && s[i] >= "0" && s[i] <= "9") { value = value * 10 + Number(s[i]); i++; } return [value, i];}// [true, 모두 몇 초] 또는 [false, 처음 틀린 자리]function parse(s) { let total = 0; let last = -1; // 바로 앞 덩이의 단위 번호 let i = 0; while (i < s.length) { const start = i; const [value, next] = readNumber(s, i); i = next; if (i === start) return [false, start + 1]; // 숫자가 와야 할 자리 if (i === s.length) return [false, i + 1]; // 단위 없이 끝났다 const u = UNITS.indexOf(s[i]); if (u < 0 || u <= last) return [false, i + 1]; if (last >= 0 && value > LIMIT[u]) return [false, start + 1]; // 수가 시작한 자리 total += value * SECONDS[u]; last = u; i++; } return [true, total];}const n = Number(lines[0]);const out = [];for (let k = 1; k <= n; k++) { const [ok, x] = parse(lines[k]); out.push((ok ? "ok " : "error ") + x);}console.log(out.join("\n"));
TypeScriptmain.ts · 45 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").trim().split(/\s+/);const UNITS = "dhms"; // 큰 단위부터const SECONDS: number[] = [86400, 3600, 60, 1];const LIMIT: number[] = [0, 23, 59, 59]; // 첫 덩이가 아닐 때 넘으면 안 되는 값// i번 자리부터 숫자를 읽어 [값, 숫자가 끝난 다음 자리]를 튜플로 돌려준다function readNumber(s: string, i: number): [number, number] { let value = 0; while (i < s.length && s[i] >= "0" && s[i] <= "9") { value = value * 10 + Number(s[i]); i++; } return [value, i];}// [true, 모두 몇 초] 또는 [false, 처음 틀린 자리]function parse(s: string): [boolean, number] { let total = 0; let last = -1; // 바로 앞 덩이의 단위 번호 let i = 0; while (i < s.length) { const start = i; const [value, next] = readNumber(s, i); i = next; if (i === start) return [false, start + 1]; // 숫자가 와야 할 자리 if (i === s.length) return [false, i + 1]; // 단위 없이 끝났다 const u = UNITS.indexOf(s[i]); if (u < 0 || u <= last) return [false, i + 1]; if (last >= 0 && value > LIMIT[u]) return [false, start + 1]; // 수가 시작한 자리 total += value * SECONDS[u]; last = u; i++; } return [true, total];}const n = Number(lines[0]);const out: string[] = [];for (let k = 1; k <= n; k++) { const [ok, x] = parse(lines[k]); out.push((ok ? "ok " : "error ") + x);}console.log(out.join("\n"));
JavaMain.java · 53 줄
import java.io.*;public class Main { static final String UNITS = "dhms"; // 큰 단위부터 static final long[] SECONDS = {86400, 3600, 60, 1}; static final long[] LIMIT = {0, 23, 59, 59}; // 첫 덩이가 아닐 때 넘으면 안 되는 값 // Java 메서드는 값 하나만 돌려주니 record로 묶는다 record Num(long value, int next) {} record Result(boolean ok, long x) {} // i번 자리부터 숫자를 읽어 (값, 숫자가 끝난 다음 자리)를 돌려준다 static Num readNumber(String s, int i) { long value = 0; while (i < s.length() && Character.isDigit(s.charAt(i))) { value = value * 10 + (s.charAt(i) - '0'); i++; } return new Num(value, i); } // (true, 모두 몇 초) 또는 (false, 처음 틀린 자리) static Result parse(String s) { long total = 0; int last = -1; // 바로 앞 덩이의 단위 번호 int i = 0; while (i < s.length()) { int start = i; Num num = readNumber(s, i); i = num.next(); if (i == start) return new Result(false, start + 1); // 숫자가 와야 할 자리 if (i == s.length()) return new Result(false, i + 1); // 단위 없이 끝났다 int u = UNITS.indexOf(s.charAt(i)); if (u < 0 || u <= last) return new Result(false, i + 1); if (last >= 0 && num.value() > LIMIT[u]) return new Result(false, start + 1); // 수가 시작한 자리 total += num.value() * SECONDS[u]; last = u; i++; } return new Result(true, total); } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(in.readLine().trim()); StringBuilder out = new StringBuilder(); for (int k = 0; k < n; k++) { Result r = parse(in.readLine().trim()); out.append(r.ok() ? "ok " : "error ").append(r.x()).append('\n'); } System.out.print(out); }}
C#Main.cs · 56 줄
using System;using System.Text;class Program{ const string Units = "dhms"; // 큰 단위부터 static readonly long[] Seconds = { 86400, 3600, 60, 1 }; static readonly long[] Limit = { 0, 23, 59, 59 }; // 첫 덩이가 아닐 때 넘으면 안 되는 값 // i번 자리부터 숫자를 읽어 (값, 숫자가 끝난 다음 자리)를 튜플로 돌려준다 static (long value, int next) ReadNumber(string s, int i) { long value = 0; while (i < s.Length && s[i] >= '0' && s[i] <= '9') { value = value * 10 + (s[i] - '0'); i++; } return (value, i); } // (true, 모두 몇 초) 또는 (false, 처음 틀린 자리) static (bool ok, long x) Parse(string s) { long total = 0; int last = -1; // 바로 앞 덩이의 단위 번호 int i = 0; while (i < s.Length) { int start = i; long value; (value, i) = ReadNumber(s, i); if (i == start) return (false, start + 1); // 숫자가 와야 할 자리 if (i == s.Length) return (false, i + 1); // 단위 없이 끝났다 int u = Units.IndexOf(s[i]); if (u < 0 || u <= last) return (false, i + 1); if (last >= 0 && value > Limit[u]) return (false, start + 1); // 수가 시작한 자리 total += value * Seconds[u]; last = u; i++; } return (true, total); } static void Main() { int n = int.Parse(Console.ReadLine().Trim()); var sb = new StringBuilder(); for (int k = 0; k < n; k++) { var (ok, x) = Parse(Console.ReadLine().Trim()); sb.Append(ok ? "ok " : "error ").Append(x).Append('\n'); } Console.Write(sb); }}
C++main.cpp · 54 줄
#include <iostream>#include <string>#include <utility>using namespace std;const string UNITS = "dhms"; // 큰 단위부터const long long SECONDS[] = {86400, 3600, 60, 1};const long long LIMIT[] = {0, 23, 59, 59}; // 첫 덩이가 아닐 때 넘으면 안 되는 값// i번 자리부터 숫자를 읽어 (값, 숫자가 끝난 다음 자리)를 pair로 돌려준다pair<long long, int> readNumber(const string& s, int i) { long long value = 0; while (i < (int)s.size() && s[i] >= '0' && s[i] <= '9') { value = value * 10 + (s[i] - '0'); i++; } return {value, i};}// (true, 모두 몇 초) 또는 (false, 처음 틀린 자리)pair<bool, long long> parse(const string& s) { int len = (int)s.size(); long long total = 0; int last = -1; // 바로 앞 덩이의 단위 번호 int i = 0; while (i < len) { int start = i; auto [value, next] = readNumber(s, i); // C++17 구조적 바인딩 i = next; if (i == start) return {false, start + 1}; // 숫자가 와야 할 자리 if (i == len) return {false, i + 1}; // 단위 없이 끝났다 int u = (int)UNITS.find(s[i]); // 없으면 npos → -1 if (u < 0 || u <= last) return {false, i + 1}; if (last >= 0 && value > LIMIT[u]) return {false, start + 1}; // 수가 시작한 자리 total += value * SECONDS[u]; last = u; i++; } return {true, total};}int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; string s; for (int k = 0; k < n; k++) { cin >> s; auto [ok, x] = parse(s); cout << (ok ? "ok " : "error ") << x << "\n"; } return 0;}
Cmain.c · 54 줄
#include <stdio.h>#include <string.h>static const char UNITS[] = "dhms"; /* 큰 단위부터 */static const long long SECONDS[] = {86400, 3600, 60, 1};static const long long LIMIT[] = {0, 23, 59, 59}; /* 첫 덩이가 아닐 때 넘으면 안 되는 값 *//* i번 자리부터 숫자를 읽어 값을 돌려주고, 숫자가 끝난 다음 자리는 *next에 써 준다. C 함수는 값 하나만 돌려주니 둘째 값은 포인터로 내보낸다 */static long long read_number(const char *s, int i, int *next) { long long value = 0; while (s[i] >= '0' && s[i] <= '9') { value = value * 10 + (s[i] - '0'); i++; } *next = i; return value;}/* 맞으면 1을 돌려주고 *x에 모두 몇 초인지, 틀리면 0을 돌려주고 *x에 처음 틀린 자리 */static int parse(const char *s, long long *x) { int len = (int)strlen(s); long long total = 0; int last = -1; /* 바로 앞 덩이의 단위 번호 */ int i = 0; while (i < len) { int start = i; long long value = read_number(s, i, &i); if (i == start) { *x = start + 1; return 0; } /* 숫자가 와야 할 자리 */ if (i == len) { *x = i + 1; return 0; } /* 단위 없이 끝났다 */ const char *p = strchr(UNITS, s[i]); int u = p ? (int)(p - UNITS) : -1; if (u < 0 || u <= last) { *x = i + 1; return 0; } if (last >= 0 && value > LIMIT[u]) { *x = start + 1; return 0; } /* 수가 시작한 자리 */ total += value * SECONDS[u]; last = u; i++; } *x = total; return 1;}int main(void) { int n; char s[64]; if (scanf("%d", &n) != 1) return 0; for (int k = 0; k < n; k++) { long long x; if (scanf("%63s", s) != 1) return 0; if (parse(s, &x)) printf("ok %lld\n", x); else printf("error %lld\n", x); } return 0;}
Craykonmain.cray · 65 줄
단위들 = "dhms"; // 큰 단위부터초 = [86400, 3600, 60, 1];한도 = [0, 23, 59, 59]; // 첫 덩이가 아닐 때 넘으면 안 되는 값// i번 자리부터 숫자를 읽어 [값, 숫자가 끝난 다음 자리]를 목록으로 돌려준다수읽기(s, i){ 값 = 0; 동안 (i < s.길이 && s[i] >= "0" && s[i] <= "9") { 값 = 값 * 10 + s[i].수로변환(); i = i + 1; } 돌려주기 [값, i];}// [참, 모두 몇 초] 또는 [거짓, 처음 틀린 자리]풀기(s){ 합 = 0; 앞단위 = -1; // 바로 앞 덩이의 단위 번호 i = 0; 동안 (i < s.길이) { 시작 = i; r = 수읽기(s, i); 값 = r[0]; i = r[1]; 만약 (i == 시작) { 돌려주기 [거짓, 시작 + 1]; // 숫자가 와야 할 자리 } 만약 (i == s.길이) { 돌려주기 [거짓, i + 1]; // 단위 없이 끝났다 } u = 단위들.자리찾기(s[i]); 만약 (u < 0 || u <= 앞단위) { 돌려주기 [거짓, i + 1]; } 만약 (앞단위 >= 0 && 값 > 한도[u]) { 돌려주기 [거짓, 시작 + 1]; // 수가 시작한 자리 } 합 = 합 + 값 * 초[u]; 앞단위 = u; i = i + 1; } 돌려주기 [참, 합];}n = 콘솔.줄읽기().수로변환();반복 (k = 0; k < n; k++){ r = 풀기(콘솔.줄읽기().공백제거()); 만약 (r[0]) { 콘솔.쓰기("ok " + r[1]); } 아니면 { 콘솔.쓰기("error " + r[1]); }}