{ }codelper
返回多个值遍历字符串64 位整数P0366

读取用时写法

给出 n 个像 1h30m 这样的用时写法,每行一个。
写法由一个或多个“块”不带空格地连接而成,一个块是“数”后面跟一个字母的“单位”。
数由一个或多个数字(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
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 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++ 用 auto [value, next] = readNumber(s, i);(C++17)接收 std::pair<long long, int>。
Java 只能返回一个值,所以用 record Num(long value, int next) {} 打包,再用 num.value() 取出。
C 也只能返回一个值,所以值用 return 返回,下一个位置写进指针 int *next。
Craykon 返回列表 返回 [值, 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、Craykon 的数在 2^53(约 9 × 10^15)以内是精确的,所以直接就对。

Pythonmain.py · 50 行
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 行
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]);    }}