如果可用的数字有 b 个,号码牌就是把 b 进制数每一位的值换成可用数字写出来的结果。 只去掉 4 时是 9 进制,位值 0 ~ 8 对应数字 0, 1, 2, 3, 5, 6, 7, 8, 9。 第一个例子的 L 50 位值是 4, 0,所以是 4 × 9 + 0 = 36。 陷阱一:如果去掉了 0,就不是普通的 b 进制了。 这时是位值为 1 ~ b 的“没有 0 的 b 进制”,余数为 0 时要用位值 b,先从 k 中减去 b 再除。 相当于向高位借了一个。 在只用 1 和 2 的第二个例子里,按普通 2 进制转换的话 I 2 会得到 21 而不是 2;不借位直接除的话会得到 12。 陷阱二:余数是从低位开始出来的。 不反转的话,第一个例子的 I 9 会得到 01 而不是 10。 陷阱三:号码牌从 1 开始。 即使这栋楼使用 0,0 也不是号码牌,所以直接把 k 转成 b 进制。 如果转换 k - 1,第一个例子的 I 4 会得到 3 而不是 5。 陷阱四:号码牌最多有 50 位,不能当作数来读。 JS 的 Number(x) 从大约 16 位起就会丢掉低位,第二个例子的最后一行会出错。 解答在八种语言里都把号码牌当作字符串读入,逐个字符处理。 陷阱五:位次最大到 10^15,放进 32 位的 int 会溢出。 在 Java 里用 int 累加的话,第二个例子的最后一行会变成 -1530494977。 解答在 C、C++ 中用 long long,在 Java、C# 中用 long。 JS、TS 的 number 和 Craykon 的数在 2^53 以内是精确的,所以 10^15 没问题;Python 的整数没有大小限制。 商在 Python 中是 k // b,JS、TS 是 Math.floor(k / b),Java、C#、C、C++ 是整数之间的 k / b,Craykon 是 数学.商(k, b)。 查一个数字对应第几个位值:Python allowed.index(ch),JS、TS、Java indexOf,C# IndexOf,C strchr,C++ find,Craykon 查找位置。
Pythonmain.py · 45 行
import sysdef main(): lines = sys.stdin.read().split("\n") banned = lines[0].strip() allowed = [d for d in "0123456789" if d not in banned] # 쓰는 숫자, 작은 것부터 b = len(allowed) has_zero = allowed[0] == "0" def to_label(k): digits = [] while k > 0: r = k % b if has_zero: # 보통의 b진법: 나머지 0 ~ b-1이 그대로 자릿값이다 digits.append(allowed[r]) k //= b else: # 0이 없는 b진법: 자릿값은 1 ~ b. 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if r == 0: r = b digits.append(allowed[r - 1]) k = (k - r) // b return "".join(reversed(digits)) # 낮은 자리부터 나왔으니 뒤집는다 def to_index(label): k = 0 for ch in label: value = allowed.index(ch) if has_zero else allowed.index(ch) + 1 k = k * b + value return k q = int(lines[1]) out = [] for line in lines[2:2 + q]: kind, arg = line.split() if kind == "I": out.append(to_label(int(arg))) else: out.append(str(to_index(arg))) # 번호표는 수로 바꾸지 않고 문자열 그대로 본다 print("\n".join(out))main()
JavaScriptmain.js · 40 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const banned = lines[0].trim();const allowed = [..."0123456789"].filter((d) => !banned.includes(d)); // 쓰는 숫자, 작은 것부터const b = allowed.length;const hasZero = allowed[0] === "0";function toLabel(k) { const digits = []; while (k > 0) { let r = k % b; if (hasZero) { // 보통의 b진법 digits.push(allowed[r]); k = Math.floor(k / b); } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r === 0) r = b; digits.push(allowed[r - 1]); k = (k - r) / b; } } return digits.reverse().join("");}function toIndex(label) { let k = 0; for (const ch of label) { k = k * b + allowed.indexOf(ch) + (hasZero ? 0 : 1); } return k;}const q = Number(lines[1]);const out = [];for (let i = 2; i < 2 + q; i++) { const [kind, arg] = lines[i].split(" "); // 차례 번호는 10^15 이하라 Number로 정확하다. 번호표는 50자리까지라 문자열 그대로 본다 out.push(kind === "I" ? toLabel(Number(arg)) : String(toIndex(arg)));}console.log(out.join("\n"));
TypeScriptmain.ts · 40 行
const lines: string[] = require("fs").readFileSync(0, "utf8").split("\n");const banned: string = lines[0].trim();const allowed: string[] = [..."0123456789"].filter((d) => !banned.includes(d)); // 쓰는 숫자, 작은 것부터const b: number = allowed.length;const hasZero: boolean = allowed[0] === "0";function toLabel(k: number): string { const digits: string[] = []; while (k > 0) { let r = k % b; if (hasZero) { // 보통의 b진법 digits.push(allowed[r]); k = Math.floor(k / b); } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r === 0) r = b; digits.push(allowed[r - 1]); k = (k - r) / b; } } return digits.reverse().join("");}function toIndex(label: string): number { let k = 0; for (const ch of label) { k = k * b + allowed.indexOf(ch) + (hasZero ? 0 : 1); } return k;}const q: number = Number(lines[1]);const out: string[] = [];for (let i = 2; i < 2 + q; i++) { const [kind, arg] = lines[i].split(" "); // 차례 번호는 10^15 이하라 number로 정확하다. 번호표는 50자리까지라 문자열 그대로 본다 out.push(kind === "I" ? toLabel(Number(arg)) : String(toIndex(arg)));}console.log(out.join("\n"));
JavaMain.java · 58 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;public class Main { static String allowed; // 쓰는 숫자, 작은 것부터 static int b; static boolean hasZero; static String toLabel(long k) { StringBuilder sb = new StringBuilder(); while (k > 0) { int r = (int) (k % b); if (hasZero) { // 보통의 b진법 sb.append(allowed.charAt(r)); k /= b; } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r == 0) r = b; sb.append(allowed.charAt(r - 1)); k = (k - r) / b; } } return sb.reverse().toString(); } static long toIndex(String label) { long k = 0; // 10^15까지라 int로는 모자란다 for (int i = 0; i < label.length(); i++) { k = k * b + allowed.indexOf(label.charAt(i)) + (hasZero ? 0 : 1); } return k; } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String banned = br.readLine().trim(); StringBuilder a = new StringBuilder(); for (char d = '0'; d <= '9'; d++) { if (banned.indexOf(d) < 0) a.append(d); } allowed = a.toString(); b = allowed.length(); hasZero = allowed.charAt(0) == '0'; int q = Integer.parseInt(br.readLine().trim()); StringBuilder out = new StringBuilder(); for (int i = 0; i < q; i++) { String[] parts = br.readLine().split(" "); if (parts[0].equals("I")) { out.append(toLabel(Long.parseLong(parts[1]))).append('\n'); } else { out.append(toIndex(parts[1])).append('\n'); } } System.out.print(out); }}
C#Main.cs · 64 行
using System;using System.Text;class Program{ static string allowed = ""; // 쓰는 숫자, 작은 것부터 static int b; static bool hasZero; static string ToLabel(long k) { var digits = new StringBuilder(); while (k > 0) { int r = (int)(k % b); if (hasZero) { // 보통의 b진법 digits.Insert(0, allowed[r]); k /= b; } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r == 0) r = b; digits.Insert(0, allowed[r - 1]); k = (k - r) / b; } } return digits.ToString(); } static long ToIndex(string label) { long k = 0; // 10^15까지라 int로는 모자란다 foreach (char ch in label) { k = k * b + allowed.IndexOf(ch) + (hasZero ? 0 : 1); } return k; } static void Main() { string banned = Console.ReadLine().Trim(); var a = new StringBuilder(); for (char d = '0'; d <= '9'; d++) { if (banned.IndexOf(d) < 0) a.Append(d); } allowed = a.ToString(); b = allowed.Length; hasZero = allowed[0] == '0'; int q = int.Parse(Console.ReadLine()); var sb = new StringBuilder(); for (int i = 0; i < q; i++) { string[] parts = Console.ReadLine().Split(' '); if (parts[0] == "I") sb.Append(ToLabel(long.Parse(parts[1]))).Append('\n'); else sb.Append(ToIndex(parts[1])).Append('\n'); } Console.Write(sb); }}
C++main.cpp · 57 行
#include <algorithm>#include <iostream>#include <string>using namespace std;string allowed; // 쓰는 숫자, 작은 것부터int b;bool hasZero;string toLabel(long long k) { string digits; while (k > 0) { int r = static_cast<int>(k % b); if (hasZero) { // 보통의 b진법 digits += allowed[r]; k /= b; } else { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 if (r == 0) r = b; digits += allowed[r - 1]; k = (k - r) / b; } } reverse(digits.begin(), digits.end()); return digits;}long long toIndex(const string& label) { long long k = 0; // 10^15까지라 int로는 모자란다 for (char ch : label) { k = k * b + static_cast<long long>(allowed.find(ch)) + (hasZero ? 0 : 1); } return k;}int main() { ios::sync_with_stdio(false); cin.tie(nullptr); string banned; cin >> banned; for (char d = '0'; d <= '9'; d++) { if (banned.find(d) == string::npos) allowed += d; } b = static_cast<int>(allowed.size()); hasZero = allowed[0] == '0'; int q; cin >> q; for (int i = 0; i < q; i++) { char kind; string arg; // 번호표는 50자리까지라 수가 아닌 문자열로 읽는다 cin >> kind >> arg; if (kind == 'I') cout << toLabel(stoll(arg)) << '\n'; else cout << toIndex(arg) << '\n'; } return 0;}
Cmain.c · 63 行
#include <stdio.h>#include <string.h>static char allowed[11]; /* 쓰는 숫자, 작은 것부터 */static int b;static int has_zero;/* k번째 번호표를 out에 적는다 */static void to_label(long long k, char *out) { char rev[64]; int len = 0; while (k > 0) { int r = (int)(k % b); if (has_zero) { /* 보통의 b진법 */ rev[len++] = allowed[r]; k /= b; } else { /* 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 */ if (r == 0) r = b; rev[len++] = allowed[r - 1]; k = (k - r) / b; } } for (int i = 0; i < len; i++) out[i] = rev[len - 1 - i]; out[len] = '\0';}static long long to_index(const char *label) { long long k = 0; /* 10^15까지라 int로는 모자란다 */ for (int i = 0; label[i] != '\0'; i++) { int value = (int)(strchr(allowed, label[i]) - allowed); k = k * b + value + (has_zero ? 0 : 1); } return k;}int main(void) { char banned[16]; if (scanf("%15s", banned) != 1) return 0; for (char d = '0'; d <= '9'; d++) { if (strchr(banned, d) == NULL) allowed[b++] = d; } allowed[b] = '\0'; has_zero = allowed[0] == '0'; int q; if (scanf("%d", &q) != 1) return 0; for (int i = 0; i < q; i++) { char kind; char arg[64]; /* 번호표는 50자리까지라 수가 아닌 문자열로 읽는다 */ if (scanf(" %c %63s", &kind, arg) != 2) return 0; if (kind == 'I') { long long k; char label[64]; sscanf(arg, "%lld", &k); to_label(k, label); printf("%s\n", label); } else { printf("%lld\n", to_index(arg)); } } return 0;}
Craykonmain.cray · 68 行
뺀숫자 = 控制台.读行().去空白();// 쓰는 숫자를 작은 것부터 이어 붙인 문자열쓰는숫자 = "";循环 (d = 0; d <= 9; d++){ 如果 (!뺀숫자.包含(d + "")) { 쓰는숫자 = 쓰는숫자 + d; }}b = 쓰는숫자.长度;영있나 = 쓰는숫자.截取(0, 1) == "0";번호표로(k){ 결과 = ""; 当 (k > 0) { r = k % b; 如果 (영있나) { // 보통의 b진법 결과 = 쓰는숫자.截取(r, 1) + 결과; k = 数学.商(k, b); } 否则 { // 0이 없는 b진법: 나머지 0은 자릿값 b로 치고 윗자리에서 하나를 빌린다 如果 (r == 0) { r = b; } 결과 = 쓰는숫자.截取(r - 1, 1) + 결과; k = 数学.商(k - r, b); } } 返回 결과;}차례로(번호표){ k = 0; 循环 (i = 0; i < 번호표.长度; i++) { 값 = 쓰는숫자.查找位置(번호표.截取(i, 1)); 如果 (!영있나) { 값 += 1; } k = k * b + 값; } 返回 k;}q = 控制台.读行().转数字();循环 (i = 0; i < q; i++){ 칸 = 控制台.读行().分割(" "); 如果 (칸[0] == "I") { 控制台.输出(번호표로(칸[1].转数字())); } 否则 { // 번호표는 50자리까지라 수로 바꾸지 않고 문자열 그대로 본다 控制台.输出(차례로(칸[1]) + ""); }}