{ }codelper
returning several valuesiterating strings64-bit integersP0366

Reading duration notation

You are given n duration notations such as 1h30m, one per line. A notation is one or more 'chunks' joined without spaces, and a chunk is a 'number' followed by a one-letter 'unit'. A number is written with one or more digits (0–9) and may have leading zeros (007 is 7). There are four units: d (day, 86400 seconds), h (hour, 3600 seconds), m (minute, 60 seconds) and s (second). Read the notation chunk by chunk from the left and check the following rules in order. Rule 1: the character where a chunk starts must be a digit.
Otherwise that position is the wrong position. Rule 2: a character must follow the number.
If the notation ends with the number, the wrong position is (length of the notation + 1). Rule 3: the character after the number must be one of d, h, m, s.
Otherwise the position of that character is the wrong position. Rule 4: from the second chunk on, the unit must be smaller than the unit of the chunk just before it (d > h > m > s).
If it is the same or larger, the position of that unit letter is the wrong position. Rule 5: from the second chunk on, the number must be at most 23 when the unit is h, at most 59 when it is m and at most 59 when it is s.
If it is larger, the position where that number starts is the wrong position. The number of the first chunk may be anything (90m is a correct notation). If one chunk breaks several rules, use the wrong position of the rule with the smallest number, and stop reading once a wrong chunk is met. Positions count the first character of the notation as 1. For each notation print one line: "ok T" if it is correct (T is the sum of number × seconds of the unit over all chunks), or "error p" if it is wrong (p is the wrong position). Try writing a function that reads a number and returns both 'the value read' and 'the position just after the number', and a function that checks one notation and returns both 'whether it is correct' and 'the seconds or the wrong position' (grading looks only at the output).

Input
The first line gives n (1 ≤ n ≤ 1,000), and each of the next n lines gives one notation.
A notation has length 1 to 40 and consists only of digits and lowercase English letters.
A run of consecutive digits has length at most 9
Output
n lines, each "ok T" or "error p".
T and p are written as plain digits without commas
Example 1 · stdin
6
1h30m
90m
1h75m
30000d
15
1m2h
stdout
ok 5400
ok 5400
error 3
ok 2592000000
error 3
error 4
Example 2 · stdin
5
1h2h
0d007h
h5
2d24h
1h30x
stdout
error 4
ok 25200
error 1
error 3
error 5
main.py
Input (stdin)

When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).

$ Ready

Open this on a computer to write and run code. On mobile you can read the problem and the solutions.

In other languagesShow solutions by language · 8 languages

After reading a number you need to know two things: the value, and how far you read (the next position).
A function that returns a single value loses one of them, and languages differ in how they return two values together.
Python returns a tuple return value, i and receives it with value, i = read_number(s, i).
JavaScript returns an array return [value, i] and unpacks it with const [value, next] = readNumber(s, i); TypeScript writes the return type as the tuple [number, number].
C# returns the tuple (long value, int next) and can unpack it straight into existing variables with (value, i) = ReadNumber(s, i).
C++ receives a std::pair<long long, int> with auto [value, next] = readNumber(s, i); (C++17).
Java returns only one value, so it bundles them in record Num(long value, int next) {} and reads num.value().
C also returns only one value, so the value goes through return and the next position is written through a pointer int *next.
Craykon returns a list return [value, i]; and reads it as r[0], r[1].
The function that checks one notation returns its pair (correct or not, seconds or wrong position) in the same way.
Trap one: after the number is read, the position has already moved on to the unit.
Unless you keep where the number started, rule 5 reports the unit's position.
For 1h75m in example 1 the answer is 3, where 75 starts, but you get 5.
Trap two: the limits apply from the second chunk on.
Applying them to the first chunk turns 90m in example 1 into error 1.
Trap three: check that a character is left after the number.
Reading s[i] for 15 in example 1 stops Python with an IndexError (Java and C# also stop with an exception, JavaScript gives undefined, and C reads the terminating '\0').
The answer is length + 1, error 3.
Trap four: rule 4 says 'smaller', so the same unit is not allowed either.
Writing < instead of <= turns 1h2h in example 2 into ok 10800.
Trap five: the sum grows up to 86,399,999,999,999 seconds for 999999999d23h59m59s.
Even 30000d in example 1 is 2,592,000,000 seconds, more than an int (about 2.1 billion) holds, so adding in a Java or C# int prints -1702967296 (in C and C++ it is undefined behaviour).
Use long in Java and C#, long long in C and C++.
Python integers have no size limit, and numbers in JavaScript, TypeScript and Craykon are exact up to 2^53 (about 9 × 10^15), so they are right as they are.

Pythonmain.py · 50 lines
import sys UNITS = "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, i  def 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, total  def 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 lines
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 lines
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 lines
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 lines
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 lines
#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 lines
#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 lines
단위들 = "dhms"; // 큰 단위부터초 = [86400, 3600, 60, 1];한도 = [0, 23, 59, 59]; // 첫 덩이가 아닐 때 넘으면 안 되는 값 // i번 자리부터 숫자를 읽어 [값, 숫자가 끝난 다음 자리]를 목록으로 돌려준다수읽기(s, i){    값 = 0;    while (i < s.Length && s[i] >= "0" && s[i] <= "9")    {        값 = 값 * 10 + s[i].ToNumber();        i = i + 1;    }    return [값, i];} // [참, 모두 몇 초] 또는 [거짓, 처음 틀린 자리]풀기(s){    합 = 0;    앞단위 = -1; // 바로 앞 덩이의 단위 번호    i = 0;    while (i < s.Length)    {        시작 = i;        r = 수읽기(s, i);        값 = r[0];        i = r[1];        if (i == 시작)        {            return [false, 시작 + 1]; // 숫자가 와야 할 자리        }        if (i == s.Length)        {            return [false, i + 1]; // 단위 없이 끝났다        }        u = 단위들.IndexOf(s[i]);        if (u < 0 || u <= 앞단위)        {            return [false, i + 1];        }        if (앞단위 >= 0 && 값 > 한도[u])        {            return [false, 시작 + 1]; // 수가 시작한 자리        }        합 = 합 + 값 * 초[u];        앞단위 = u;        i = i + 1;    }    return [true, 합];} n = Console.ReadLine().ToNumber();for (k = 0; k < n; k++){    r = 풀기(Console.ReadLine().Trim());    if (r[0])    {        Console.WriteLine("ok " + r[1]);    }    else    {        Console.WriteLine("error " + r[1]);    }}