Đọc số điện thoại
Đề bài
Điện thoại của Lan có tính năng đọc to số của người đang gọi đến: nó đọc từng chữ số một. Ví dụ số $0942250787$ sẽ được đọc là 0 9 4 2 2 5 0 7 8 7.
Cho một số điện thoại gồm đúng $10$ chữ số, hãy in ra lần lượt các chữ số của nó, mỗi chữ số cách nhau một dấu cách.
Dữ liệu vào: Một dòng chứa số điện thoại $N$ gồm đúng $10$ chữ số (có thể bắt đầu bằng chữ số $0$).
Kết quả: In ra $10$ chữ số của $N$ theo đúng thứ tự, cách nhau bởi một dấu cách.
Giới hạn:
- $N$ có đúng $10$ chữ số, các chữ số từ $0$ đến $9$ (kể cả chữ số $0$ ở đầu).
Giới hạn thời gian: 1 giây, bộ nhớ: 256 MB. Dữ liệu nhập từ bàn phím và in ra màn hình.
01674696660 1 6 7 4 6 9 6 6 609422507870 9 4 2 2 5 0 7 8 7Nguồn: phỏng theo bài "QMOBILE" – vinhdinhcoder.net.
Lời mở đầu
Đề nói "cho một số điện thoại", thế là rất nhiều bạn phản xạ ngay long long n; cin >> n;. Nhưng số điện thoại không phải là "số" để làm toán – nó là một dãy ký tự chữ số, và chữ số 0 ở đầu là một phần quan trọng của nó. Bài này dạy đúng một điều: chọn kiểu dữ liệu theo ý nghĩa của dữ liệu. Mình sẽ cho các em xem code bẫy, rồi hai cách làm đúng: đọc xâu, và (cho bạn nào thích) đọc số nhưng tách đúng 10 chữ số.
Link này chỉ học viên đã tham gia tổ chức mới vào được. Học viên nên ưu tiên nộp ở đây vì sẽ được chấm nhanh hơn.
Nộp bài trên CLUE OJLộ trình lời giải, bấm vào từng phần để nhảy tới:
Bộ testBộ test chính thức có gì?
Bộ test có 20 test, bài không chia subtask. Mỗi test là một xâu đúng 10 chữ số. Mình đếm số chữ số 0 ở đầu của từng test:
| Số chữ số 0 ở đầu | 0 | 1 | 2 | 3 | 5 | 9 | 10 |
|---|---|---|---|---|---|---|---|
| Số test | 3 | 9 | 1 | 3 | 2 | 1 | 1 |
- Có tới 17/20 test bắt đầu bằng chữ số 0 (có test toàn số 0
0000000000, có test chỉ có chữ số cuối khác 0). Chỉ có test 4, 6, 10 là không có số 0 ở đầu. - Có 2 test (test 4, 10) có giá trị lớn hơn $2^{31} - 1 = 2\,147\,483\,647$ – nếu đọc bằng
intthì tràn.
Ý tưởngSố điện thoại là xâu, không phải số
Khi đọc 0942250787 vào một biến số nguyên, máy chỉ lưu giá trị $942\,250\,787$ – số 0 ở đầu không làm thay đổi giá trị nên bị "nuốt" mất, và máy không còn cách nào biết ban đầu có mấy số 0. Còn đọc vào một xâu (string) thì máy giữ nguyên văn từng ký tự em gõ vào.
Em nhập một số điện thoại 10 chữ số rồi bấm Tiếp để so sánh hai cách đọc:
BẫyĐọc thành số rồi in lại
cin >> nđọc0942250787thành giá trị $942\,250\,787$;to_string(n)cho xâu"942250787"chỉ có 9 ký tự → in ra thiếu số 0 đầu.- Test
0000000000còn tệ hơn: $n = 0$, chỉ in ra đúng một chữ số0thay vì mười. - Bản Python
int(input())mắc y hệt lỗi này.
Kết quả chạy thật: bản C++ đúng 3/20 test, sai ở test 1–3, 5, 7–9, 11–20; test đúng chạy lâu nhất 0.00s – chỉ đúng 3 test không có số 0 ở đầu. Bản Python đúng 3/20 test, sai ở test 1–3, 5, 7–9, 11–20; test đúng chạy lâu nhất 0.01s.
Cách 1Đọc xâu, in từng ký tự
Đọc vào string s, rồi duyệt từng ký tự s[i], in kèm một dấu cách.
cin >> sđọc nguyên văn "từ"0942250787– mỗi ký tự là một chữ số, kể cả các số 0 ở đầu.s[i]có kiểucharnêncout << s[i]in ra đúng ký tự chữ số đó.- Sau chữ số cuối có thừa một dấu cách – trình chấm so sánh theo từng "từ" nên không sao (code đã được chấm thật, xem kết quả).
- Python:
print(*s)"rải" từng ký tự của xâu làm các tham số củaprint, vàprinttự chèn dấu cách giữa chúng.
Kết quả chạy thật: bản C++ đúng 20/20 test; test đúng chạy lâu nhất 0.00s. Bản Python đúng 20/20 test; test đúng chạy lâu nhất 0.02s. Full điểm.
Cách 2Vẫn đọc số, nhưng tách đúng 10 chữ số
Thật ra đọc vào số cũng làm được, nếu ta nhớ là số điện thoại luôn có đúng 10 chữ số: cứ lấy n % 10 (chữ số hàng đơn vị) rồi n /= 10, lặp đúng 10 lần. Khi các chữ số "thật" đã hết thì $n = 0$ và n % 10 cho ra chính các số 0 ở đầu! Các chữ số được tách từ phải sang trái nên ta lưu vào mảng d[9], d[8], …, d[0] rồi in theo thứ tự xuôi.
- Vòng lặp chạy cố định 10 lần, không dừng khi $n = 0$ – đó là mấu chốt để giữ số 0 ở đầu.
- Phải dùng
long long: số điện thoại có thể tới $9\,999\,999\,999 > 2^{31} - 1$. Mình thử đổi thànhint: bảnintđúng 18/20 test, sai ở test 4, 10; test đúng chạy lâu nhất 0.00s – sai đúng test 4, 10 có giá trị vượtint(khi số quá lớn,cinbáo lỗi và gán $n = 2\,147\,483\,647$, nên in ra2 1 4 7 4 8 3 6 4 7).
Kết quả chạy thật: bản C++ đúng 20/20 test; test đúng chạy lâu nhất 0.00s. Bản Python đúng 20/20 test; test đúng chạy lâu nhất 0.02s. Full điểm.
Tổng kếtKết quả chạy thật trên bộ test
| Code | Ngôn ngữ | Đúng | Lâu nhất | Ghi chú |
|---|---|---|---|---|
| Bẫy: đọc thành số rồi in lại | C++ | 3/20 | 0.00s | Sai test 1–3, 5, 7–9, 11–20 (mất số 0 đầu) |
| Python | 3/20 | 0.01s | Sai test 1–3, 5, 7–9, 11–20 | |
| Cách 1: đọc xâu | C++ | 20/20 | 0.00s | Full điểm |
| Python | 20/20 | 0.02s | Full điểm | |
| Cách 2: số + tách đúng 10 chữ số | C++ | 20/20 | 0.00s | Full điểm |
| Python | 20/20 | 0.02s | Full điểm | |
Cách 2 nhưng dùng int | C++ | 18/20 | 0.00s | Sai test 4, 10 (vượt int) |
Nhận xét
Đăng nhận xét