2차원 행렬로 표현된 Excel 스프레드시트가 있다고 가정해 보겠습니다. 우리의 목표는 모든 셀의 값과 수식이 계산된 결과 행렬을 구하는 것입니다. Excel 스프레드시트는 일반적으로 아래와 같은 형태를 가집니다.
| B1 | 7 | 0 |
| 3 | 5 | =A1+A2 |
문제 이해하기
스프레드시트의 열은 A, B, C...로 명명되고, 행은 1, 2, 3...으로 번호가 매겨집니다. 각 셀에는 다음 세 가지 중 하나가 들어갈 수 있습니다.
- 값: 숫자 그 자체 (예: "7", "3")
- 셀 참조: 다른 셀을 가리키는 주소 (예: "B1")
- 수식: 숫자 또는 셀 참조 간의 연산식 (예: "=A1+5", "=A2+B2", "=2+5")
예를 들어 입력이 다음과 같다면,
| B1 | 7 | 0 |
| 3 | 5 | =A1+A2 |
출력은 아래와 같습니다.
| 7 | 7 | 0 |
| 3 | 5 | 10 |
그 이유는 첫 번째 행 두 번째 열의 B1이 7이고, "=A1+A2"는 7 + 3 = 10으로 계산되기 때문입니다.
해결 접근 방법
이 문제를 해결하기 위해 다음 단계를 따릅니다.
- resolve() 함수 정의: 문자열 s를 받아 숫자라면 정수로 변환하여 반환하고, 그렇지 않으면 solve(getIdx(s))를 반환합니다.
- getIdx() 함수 정의: 셀 주소 s를 받아 [행 인덱스, 열 인덱스] 리스트를 반환합니다. 첫 번째 값은 s의 두 번째 문자부터 끝까지를 정수로 변환한 값(1을 빼서 0 기반 인덱스로 조정)이고, 두 번째 값은 s[0]의 ASCII 코드에서 'A'의 ASCII 코드를 뺀 값입니다.
- do() 함수 정의: a, b, 연산자 op를 받아 사칙연산을 수행합니다.
- op가 "+"이면 a + b를 반환합니다.
- op가 "-"이면 a - b를 반환합니다.
- op가 "*"이면 a * b를 반환합니다.
- op가 "/"이면 a / b를 반환합니다.
- solve() 함수 정의: 좌표 i, j를 받아 해당 셀의 최종 값을 재귀적으로 계산합니다.
- matrix[i][j]가 숫자라면 그 값을 그대로 반환합니다.
- 숫자가 아니라면:
- s := matrix[i][j]로 설정합니다.
- s[0]이 "="이라면 수식이므로, s의 세 번째 문자부터 끝까지 탐색하면서 +, -, /, * 중 하나의 연산자를 찾아 op에 저장하고 반복을 종료합니다.
- [a, b] := s의 두 번째 문자부터 끝까지를 op로 분리한 두 피연산자입니다.
- [aRes, bRes] := [resolve(a), resolve(b)]로 각 피연산자를 실제 값으로 변환합니다.
- do(aRes, bRes, op)를 반환합니다.
- "="로 시작하지 않는다면 셀 참조이므로 solve(getIdx(s))를 반환합니다.
- 행렬의 모든 행과 열을 순회하면서 matrix[i][j] := str(solve(i, j))로 갱신합니다.
- 최종 행렬을 반환합니다.
구현 예제
아래 구현을 통해 더 잘 이해할 수 있습니다.
class Solution:
def solve(self, matrix):
def resolve(s):
try:
return int(s)
except:
return solve(*getIdx(s))
def getIdx(s):
return [int(s[1:]) - 1, ord(s[0]) - ord("A")]
def do(a, b, op):
if op == "+":
return a + b
if op == "-":
return a - b
if op == "*":
return a * b
if op == "/":
return a / b
def solve(i, j):
try:
return int(matrix[i][j])
except:
s = matrix[i][j]
if s[0] == "=":
for c in s[2:]:
if c in "+-/*":
op = c
break
a, b = s[1:].split(op)
aRes, bRes = resolve(a), resolve(b)
return do(aRes, bRes, op)
else:
return solve(*getIdx(s))
for i in range(len(matrix)):
for j in range(len(matrix[0])):
matrix[i][j] = str(solve(i, j))
return matrix
ob = Solution()
matrix = [
["B1", "7", "0"],
["3", "5", "=A1+A2"]
]
print(ob.solve(matrix))입력
[["B1", "7", "0"], ["3", "5", "=A1+A2"] ]
출력
[['7', '7', '0'], ['3', '5', '10']]
코드 설명
이 구현의 핵심은 재귀적 해석(recursive resolution)입니다. resolve() 함수는 피연산자가 단순 숫자인지 확인하고, 숫자가 아니라면 getIdx()를 통해 셀 좌표로 변환한 뒤 solve()를 다시 호출합니다. 이 과정 덕분에 "=A1+A2"처럼 셀 참조를 포함한 수식도 참조된 셀이 또 다른 참조나 수식을 담고 있더라도 연쇄적으로 올바르게 계산됩니다.
또한 try-except 블록을 활용해 값이 정수로 변환 가능한지 검사함으로써, 별도의 타입 체크 로직 없이 숫자 여부를 간결하게 판별할 수 있다는 점도 눈여겨볼 만합니다. 마지막으로 전체 행렬을 순회하며 각 셀을 계산된 값의 문자열로 교체하면, 모든 수식이 반영된 완성된 스프레드시트를 얻게 됩니다.