{ }codelper
값 바꾸기문자열 나누기P0272

한꺼번에 바꿔 넣는 계산기 칸

작은 계산기에 영어 소문자 a부터 z까지 이름이 붙은 칸 26개가 있다.
모든 칸은 처음에 0이다. 첫 줄에 명령 수 q가, 다음 q줄에 명령이 하나씩 주어진다.
명령 안의 낱말은 빈칸 하나로 나뉜다. set x v는 칸 x에 정수 v를 넣는다. swap x y는 칸 x와 칸 y의 값을 맞바꾼다.
x와 y는 같을 수 있고, 그때는 아무것도 바뀌지 않는다. rot x y z는 서로 다른 세 칸의 값을 돌린다.
x는 y의 원래 값을, y는 z의 원래 값을, z는 x의 원래 값을 받는다. let x1 x2 … xk = e1 e2 … ek는 칸 xi에 항 ei의 값을 넣는다(i = 1, …, k).
왼쪽 칸 이름들은 서로 다르다. 이때 오른쪽의 항 k개를 모두 이 명령 직전의 값으로 먼저 계산하고, 그다음에 k칸에 한꺼번에 넣는다. 항은 칸 이름 하나, 0 이상의 정수 하나, 또는 u+w나 u-w 꼴이다.
u와 w는 각각 칸 이름이거나 0 이상의 정수이고, 사이에 빈칸이 없다. show x1 x2 … xk는 칸 x1, x2, …, xk의 값을 이 차례대로 빈칸 하나씩 띄어 한 줄에 출력한다.
같은 칸이 여러 번 나올 수도 있다. 명령을 차례대로 처리하며 show마다 한 줄씩 출력하라.

입력
1 ≤ q ≤ 2,000.
set의 v는 -1,000,000 이상 1,000,000 이하.
항 안의 정수는 0 이상 1,000,000 이하.
let과 show의 칸 수 k는 1 이상 26 이하.
show는 1개 이상 200개 이하.
모든 칸의 값은 언제나 -1,000,000,000 이상 1,000,000,000 이하다
출력
show 수만큼의 줄
예시 1 · stdin
7
set a 1
let a b = a+b a
let a b = a+b a
let a b = a+b a
show a b
swap a b
show a b
stdout
3 2
2 3
예시 2 · stdin
9
set x 5
set y -7
set z 9
swap x x
show x
rot x y z
show x y z
let x y z = z-1 x+y 100
show x y z
stdout
5
-7 9 5
4 2 100
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

두 값을 맞바꾸는 방법이 언어마다 달라요.
Python a, b = b, a, JS·TS [a, b] = [b, a], C# (a, b) = (b, a)처럼 한꺼번에 넣는 문법이 있으면 오른쪽을 모두 계산한 뒤에 넣어서 rot도 한 줄이에요.
C++은 문법 대신 std::swap(a, b) 함수가 있고, Java·C·크레이콘은 임시 변수 tmp에 하나를 맡겨 둬요.
let은 칸 수 k가 그때그때 달라서 어느 언어든 오른쪽 값을 먼저 배열(Python·크레이콘은 목록)에 모은 뒤 넣어요.
함정 ① let을 하나씩 계산하자마자 넣기: let a b = a+b a에서 a를 먼저 바꾸면 b는 새 a를 받아요.
② 복사가 아닌 이름 붙이기: Python old = reg, JS const old = reg는 같은 사전·배열을 가리켜서 미리 찍어 둔 것이 되지 않아요.
dict(reg), [...reg]처럼 복사해야 해요.
③ 임시 변수 없는 맞바꾸기 요령: XOR(a ^= b; b ^= a; a ^= b)과 더하고 빼기(a = a + b; b = a - b; a = a - b)는 두 이름이 같은 칸을 가리키면(swap x x) 값이 0이 돼요.
더하고 빼기는 큰 수에서 넘칠 수도 있어요.
④ rot을 임시 변수 없이 x = y; y = z; z = x로 쓰면 z가 이미 바뀐 x(원래 y)를 받아요.
방향도 헷갈리기 쉬워요: x가 y의 값을 받는 쪽이에요.
⑤ 항의 -는 늘 빼기예요.
항 안의 정수에는 부호가 없으니 첫 글자 뒤에서 +나 -를 찾으면 돼요.
크레이콘은 함수 안에서 값을 넣은 이름을 본문 맨 바깥에서도 쓰면(매개변수가 아니면) 그 바깥 변수가 바뀌니, 함수 안의 반복 변수 이름을 바깥과 다르게 지어요.

Pythonmain.py · 42 줄
import sys lines = sys.stdin.read().split("\n")q = int(lines[0])reg = {chr(c): 0 for c in range(ord("a"), ord("z") + 1)}  def atom(t):    return reg[t] if t.isalpha() else int(t)  def term(t):    # p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없어서 -는 늘 빼기다    for op in "+-":        i = t.find(op)        if i > 0:            a, b = atom(t[:i]), atom(t[i + 1:])            return a + b if op == "+" else a - b    return atom(t)  out = []for line in lines[1:1 + q]:    p = line.split()    cmd = p[0]    if cmd == "set":        reg[p[1]] = int(p[2])    elif cmd == "swap":        x, y = p[1], p[2]        reg[x], reg[y] = reg[y], reg[x]  # x와 y가 같아도 그대로    elif cmd == "rot":        x, y, z = p[1], p[2], p[3]        reg[x], reg[y], reg[z] = reg[y], reg[z], reg[x]    elif cmd == "let":        eq = p.index("=")        targets = p[1:eq]        values = [term(t) for t in p[eq + 1:]]  # 오른쪽을 모두 먼저 계산한다        for x, v in zip(targets, values):            reg[x] = v    else:  # show        out.append(" ".join(str(reg[x]) for x in p[1:]))print("\n".join(out))
JavaScriptmain.js · 40 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const reg = new Array(26).fill(0);const at = (x) => x.charCodeAt(0) - 97; // 'a' → 0 const atom = (t) => (/^[a-z]$/.test(t) ? reg[at(t)] : Number(t)); function term(t) {  // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다  const i = t.search(/[+-]/);  if (i <= 0) return atom(t);  const a = atom(t.slice(0, i));  const b = atom(t.slice(i + 1));  return t[i] === "+" ? a + b : a - b;} const out = [];for (const line of lines.slice(1, 1 + q)) {  const p = line.trim().split(" ");  const cmd = p[0];  if (cmd === "set") {    reg[at(p[1])] = Number(p[2]);  } else if (cmd === "swap") {    const x = at(p[1]);    const y = at(p[2]);    [reg[x], reg[y]] = [reg[y], reg[x]]; // 구조 분해로 한꺼번에 바꾼다  } else if (cmd === "rot") {    const [x, y, z] = p.slice(1).map(at);    [reg[x], reg[y], reg[z]] = [reg[y], reg[z], reg[x]];  } else if (cmd === "let") {    const eq = p.indexOf("=");    const values = p.slice(eq + 1).map(term); // 오른쪽을 모두 먼저 계산한다    p.slice(1, eq).forEach((x, i) => {      reg[at(x)] = values[i];    });  } else {    out.push(p.slice(1).map((x) => reg[at(x)]).join(" "));  }}console.log(out.join("\n"));
TypeScriptmain.ts · 42 줄
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const reg: number[] = new Array<number>(26).fill(0);const at = (x: string): number => x.charCodeAt(0) - 97; // 'a' → 0 const atom = (t: string): number => (/^[a-z]$/.test(t) ? reg[at(t)] : Number(t)); function term(t: string): number {  // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다  const i = t.search(/[+-]/);  if (i <= 0) return atom(t);  const a = atom(t.slice(0, i));  const b = atom(t.slice(i + 1));  return t[i] === "+" ? a + b : a - b;} const out: string[] = [];for (const line of lines.slice(1, 1 + q)) {  const p = line.trim().split(" ");  const cmd = p[0];  if (cmd === "set") {    reg[at(p[1])] = Number(p[2]);  } else if (cmd === "swap") {    const x = at(p[1]);    const y = at(p[2]);    [reg[x], reg[y]] = [reg[y], reg[x]]; // 구조 분해로 한꺼번에 바꾼다  } else if (cmd === "rot") {    const [x, y, z] = p.slice(1).map(at);    [reg[x], reg[y], reg[z]] = [reg[y], reg[z], reg[x]];  } else if (cmd === "let") {    const eq = p.indexOf("=");    const values: number[] = p.slice(eq + 1).map(term); // 오른쪽을 모두 먼저 계산한다    p.slice(1, eq).forEach((x, i) => {      reg[at(x)] = values[i];    });  } else {    out.push(p.slice(1).map((x) => reg[at(x)]).join(" "));  }}console.log(out.join("\n"));
JavaMain.java · 68 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringJoiner; public class Main {    static int[] reg = new int[26];     static int at(String x) {        return x.charAt(0) - 'a';    }     static int atom(String t) {        return Character.isLetter(t.charAt(0)) ? reg[at(t)] : Integer.parseInt(t);    }     static int term(String t) {        // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다        for (int i = 1; i < t.length(); i++) {            char c = t.charAt(i);            if (c == '+' || c == '-') {                int a = atom(t.substring(0, i));                int b = atom(t.substring(i + 1));                return c == '+' ? a + b : a - b;            }        }        return atom(t);    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int t = 0; t < q; t++) {            String[] p = in.readLine().trim().split(" ");            switch (p[0]) {                case "set" -> reg[at(p[1])] = Integer.parseInt(p[2]);                case "swap" -> {                    int x = at(p[1]), y = at(p[2]);                    int tmp = reg[x]; // Java에는 a, b = b, a가 없어서 임시 변수를 쓴다                    reg[x] = reg[y];                    reg[y] = tmp;                }                case "rot" -> {                    int x = at(p[1]), y = at(p[2]), z = at(p[3]);                    int tmp = reg[x];                    reg[x] = reg[y];                    reg[y] = reg[z];                    reg[z] = tmp;                }                case "let" -> {                    int eq = 1;                    while (!p[eq].equals("=")) eq++;                    int k = eq - 1;                    int[] values = new int[k]; // 오른쪽을 모두 먼저 계산해 둔다                    for (int i = 0; i < k; i++) values[i] = term(p[eq + 1 + i]);                    for (int i = 0; i < k; i++) reg[at(p[1 + i])] = values[i];                }                default -> {                    StringJoiner sj = new StringJoiner(" ");                    for (int i = 1; i < p.length; i++) sj.add(String.valueOf(reg[at(p[i])]));                    out.append(sj).append('\n');                }            }        }        System.out.print(out);    }}
C#Main.cs · 62 줄
using System;using System.Linq;using System.Text; class Program{    static int[] reg = new int[26];     static int At(string x) => x[0] - 'a';     static int Atom(string t) => char.IsLetter(t[0]) ? reg[At(t)] : int.Parse(t);     static int Term(string t)    {        // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다        int i = t.IndexOfAny(new[] { '+', '-' }, 1);        if (i < 0) return Atom(t);        int a = Atom(t.Substring(0, i));        int b = Atom(t.Substring(i + 1));        return t[i] == '+' ? a + b : a - b;    }     static void Main()    {        int q = int.Parse(Console.ReadLine().Trim());        var sb = new StringBuilder();        for (int t = 0; t < q; t++)        {            string[] p = Console.ReadLine().Trim().Split(' ');            switch (p[0])            {                case "set":                    reg[At(p[1])] = int.Parse(p[2]);                    break;                case "swap":                {                    int x = At(p[1]), y = At(p[2]);                    (reg[x], reg[y]) = (reg[y], reg[x]); // 튜플로 한꺼번에 바꾼다                    break;                }                case "rot":                {                    int x = At(p[1]), y = At(p[2]), z = At(p[3]);                    (reg[x], reg[y], reg[z]) = (reg[y], reg[z], reg[x]);                    break;                }                case "let":                {                    int eq = Array.IndexOf(p, "=");                    // 오른쪽을 모두 먼저 계산해 배열로 받아 둔다                    int[] values = p.Skip(eq + 1).Select(Term).ToArray();                    for (int i = 0; i < values.Length; i++) reg[At(p[1 + i])] = values[i];                    break;                }                default:                    sb.Append(string.Join(" ", p.Skip(1).Select(x => reg[At(x)]))).Append('\n');                    break;            }        }        Console.Write(sb);    }}
C++main.cpp · 58 줄
#include <array>#include <iostream>#include <sstream>#include <string>#include <utility>#include <vector> std::array<int, 26> reg{}; int atom(const std::string &t) {    return std::isalpha(static_cast<unsigned char>(t[0])) ? reg[t[0] - 'a'] : std::stoi(t);} int term(const std::string &t) {    // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다    std::size_t i = t.find_first_of("+-", 1);    if (i == std::string::npos) return atom(t);    int a = atom(t.substr(0, i)), b = atom(t.substr(i + 1));    return t[i] == '+' ? a + b : a - b;} int main() {    std::string line;    std::getline(std::cin, line);    int q = std::stoi(line);    std::string out;    for (int c = 0; c < q; c++) {        std::getline(std::cin, line);        std::istringstream ss(line);        std::vector<std::string> p;        for (std::string w; ss >> w;) p.push_back(w);        if (p[0] == "set") {            reg[p[1][0] - 'a'] = std::stoi(p[2]);        } else if (p[0] == "swap") {            std::swap(reg[p[1][0] - 'a'], reg[p[2][0] - 'a']);        } else if (p[0] == "rot") {            int &x = reg[p[1][0] - 'a'], &y = reg[p[2][0] - 'a'], &z = reg[p[3][0] - 'a'];            int tmp = x;            x = y;            y = z;            z = tmp;        } else if (p[0] == "let") {            std::size_t eq = 1;            while (p[eq] != "=") eq++;            std::vector<int> values; // 오른쪽을 모두 먼저 계산한다            for (std::size_t i = eq + 1; i < p.size(); i++) values.push_back(term(p[i]));            for (std::size_t i = 0; i < values.size(); i++) reg[p[1 + i][0] - 'a'] = values[i];        } else {            for (std::size_t i = 1; i < p.size(); i++) {                if (i > 1) out += ' ';                out += std::to_string(reg[p[i][0] - 'a']);            }            out += '\n';        }    }    std::cout << out;    return 0;}
Cmain.c · 60 줄
#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h> static int reg[26]; static int atom(const char *t, int len) {    if (isalpha((unsigned char)t[0])) return reg[t[0] - 'a'];    int v = 0;    for (int i = 0; i < len; i++) v = v * 10 + (t[i] - '0');    return v;} /* p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없다 */static int term(const char *t) {    int n = (int)strlen(t);    for (int i = 1; i < n; i++)        if (t[i] == '+' || t[i] == '-') {            int a = atom(t, i), b = atom(t + i + 1, n - i - 1);            return t[i] == '+' ? a + b : a - b;        }    return atom(t, n);} int main(void) {    static char line[1024];    char *tok[64];    fgets(line, sizeof line, stdin);    int q = atoi(line);    for (int c = 0; c < q; c++) {        fgets(line, sizeof line, stdin);        int k = 0;        for (char *s = strtok(line, " \r\n"); s; s = strtok(NULL, " \r\n")) tok[k++] = s;        if (strcmp(tok[0], "set") == 0) {            reg[tok[1][0] - 'a'] = atoi(tok[2]);        } else if (strcmp(tok[0], "swap") == 0) {            int x = tok[1][0] - 'a', y = tok[2][0] - 'a';            int tmp = reg[x]; /* 임시 칸에 하나를 맡겨 둔다 */            reg[x] = reg[y];            reg[y] = tmp;        } else if (strcmp(tok[0], "rot") == 0) {            int x = tok[1][0] - 'a', y = tok[2][0] - 'a', z = tok[3][0] - 'a';            int tmp = reg[x];            reg[x] = reg[y];            reg[y] = reg[z];            reg[z] = tmp;        } else if (strcmp(tok[0], "let") == 0) {            int eq = 1;            while (strcmp(tok[eq], "=") != 0) eq++;            int n = eq - 1, values[26];            for (int i = 0; i < n; i++) values[i] = term(tok[eq + 1 + i]); /* 먼저 모두 계산 */            for (int i = 0; i < n; i++) reg[tok[1 + i][0] - 'a'] = values[i];        } else {            for (int i = 1; i < k; i++) printf(i > 1 ? " %d" : "%d", reg[tok[i][0] - 'a']);            putchar('\n');        }    }    return 0;}
Craykonmain.cray · 77 줄
q = 콘솔.줄읽기().수로변환();reg = 사전();반복 (i = 0; i < 26; i++){    reg[글자.글자로변환(97 + i)] = 0;} 하나(t){    만약 (reg.있나(t))    {        돌려주기 reg[t];    }    돌려주기 t.수로변환();} // p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없다항(t){    // 함수 안에서 넣은 이름이 밖에도 있으면 밖의 변수가 바뀌니, 밖에서 안 쓰는 이름(w)을 쓴다    반복 (w = 1; w < t.길이; w++)    {        만약 (t[w] == "+" || t[w] == "-")        {            가 = 하나(t.부분문자열(0, w));            나머지 = 하나(t.부분문자열(w + 1));            만약 (t[w] == "+")            {                돌려주기 가 + 나머지;            }            돌려주기 가 - 나머지;        }    }    돌려주기 하나(t);} 반복 (c = 0; c < q; c++){    p = 콘솔.줄읽기().공백제거().나누기(" ");    선택 (p[0])    {        경우 "set":            reg[p[1]] = p[2].수로변환();            그만;        경우 "swap":            임시 = reg[p[1]]; // 크레이콘에는 한꺼번에 넣기가 없어서 임시 변수에 맡긴다            reg[p[1]] = reg[p[2]];            reg[p[2]] = 임시;            그만;        경우 "rot":            임시 = reg[p[1]];            reg[p[1]] = reg[p[2]];            reg[p[2]] = reg[p[3]];            reg[p[3]] = 임시;            그만;        경우 "let":            eq = p.자리찾기("=");            값들 = []; // 오른쪽을 모두 먼저 계산해 둔다            반복 (i = eq + 1; i < p.개수; i++)            {                값들.넣기(항(p[i]));            }            반복 (i = 0; i < 값들.개수; i++)            {                reg[p[1 + i]] = 값들[i];            }            그만;        그밖에:            보일것 = [];            반복 (i = 1; i < p.개수; i++)            {                보일것.넣기(reg[p[i]]);            }            콘솔.쓰기(보일것.잇기(" "));            그만;    }}