631. Design Excel Sum Formula
Problem Explanation
The problem is about creating a simplified Excel spreadsheet using a 2D array as the backend data structure. The height, H
, and the width, W
, of the Excel is given at the time of construction where H
is a positive integer, and W
is an uppercase letter denoting the maximum width of the Excel sheet. The user should be able to set a value to any cell in the spreadsheet by specifying the row and column. Along with that, the user should be able to get the value stored in any cell. Additionally, the program should support the capability to calculate the sum of a list of cells or a range of cells and store the sum in a particular cell.
Walkthrough of an Example
Let's go through a scenario to understand how a solution might look like.
If we create an Excel using Excel(3, 'C')
, it means we have an Excel spreadsheet with 3 rows and 3 columns, and all the cells have an initial value of 0.
A B C 1 0 0 0 2 0 0 0 3 0 0 0
If we execute Set(1, "A", 2)
, it means we are setting the cell at row 1, column 'A' to 2.
A B C 1 2 0 0 2 0 0 0 3 0 0 0
Solution Explanation
The approach to solve this problem is by using a 2D array to simulate the Excel spreadsheet. Each cell in this 2D array is an object of type Cell
. Each Cell
object stores the value of the cell and an unordered map of cells that contribute to its value. If a sum operation is being performed, then the posCount
map in the cell where the sum is being stored, will keep track of each cell's position that contributes to its value, along with the corresponding contribution count.
For example, in the initial scenario where Excel of size (3, 'C')
was created, the 2D array would be initialized to all zeros:
[ [0, 0, 0], [0, 0, 0], [0, 0, 0] ]
Python Solution
python class Cell: def __init__(self): self.val = 0 self.posCount = {} class Excel: def __init__(self, H: int, W: str): self.width = ord(W) - ord('A') + 1 self.sheet = [[Cell() for _ in range(self.width)] for _ in range(H)] def set(self, r: int, c: str, v: int) -> None: self.sheet[r-1][ord(c)-ord('A')].val = v self.sheet[r-1][ord(c)-ord('A')].posCount = {} def get(self, r: int, c: str) -> int: cell = self.sheet[r-1][ord(c)-ord('A')] return cell.val if not cell.posCount else self.sum(r, c, [f"{k[0]}{k[1]}" for k in cell.posCount.keys()]) def sum(self, r: int, c: str, strs: List[str]) -> int: self.sheet[r-1][ord(c)-ord('A')].posCount = {} total = 0 for st in strs: if ':' not in st: val = self.get(int(st[1:]), st[0]) self.sheet[r-1][ord(c)-ord('A')].posCount[(st[0], int(st[1:]))] = 1 total += val else: st_split = st.split(':') r1, c1, r2, c2 = int(st_split[0][1:]),st_split[0][0], int(st_split[1][1:]), st_split[1][0] for row in range(r1, r2+1): for col in range(ord(c1)-ord('A'), ord(c2)-ord('A')+1): total += self.sheet[row][col].val self.sheet[r-1][ord(c)-ord('A')].posCount[(chr(ord('A') + col), row+1)] = 1 self.sheet[r-1][ord(c)-ord('A')].val = total return total
In this code we use a Cell
class which will store the value of the cell along with a map posCount
of cells that contribute to its value. In the constructor we initialize width
and sheet
. In the set
method we are setting a specific cell's value and resetting its posCount
. For the get
method, if a cell's value is being calculated from other cells, we have to calculate it, otherwise return the value of the cell directly. The sum
method updates the posCount
for a cell whose value will be computed by the sum of other cells or ranges of cells. It returns this sum.
JavaScript Solution
javascript
class Cell {
constructor() {
this.val = 0;
this.posCount = {};;
}
}
class Excel {
constructor(H, W) {
this.width = W.charCodeAt(0) - 'A'.charCodeAt(0) + 1;
this.sheet = new Array(H).fill(0).map(() => new Array(this.width).fill(new Cell()));
}
set(r, c, v) {
this.sheet[r][c.charCodeAt(0) - 'A'.charCodeAt(0)] = new Cell();
this.sheet[r][c.charCodeAt(0) -'A'.charCodeAt(0)].val = v;
}
get(r, c) {
let cell = this.sheet[r][c.charCodeAt(0) - 'A'.charCodeAt(0)];
if (!cell.posCount) {
return cell.val;
} else {
return this.sum(r, c, Object.keys(cell.posCount));
}
}
sum(r, c, strs) {
this.sheet[r][c.charCodeAt(0) - 'A'.charCodeAt(0)] = new Cell();
let total = 0;
for (let str of strs) {
if (!str.includes(":")) {
let val = this.get(Number(str.slice(1)), str[0]);
this.sheet[r][c.charCodeAt(0) - 'A'.charCodeAt(0)].posCount[str] = 1;
total += val;
} else {
let [start, end] = str.split(":");
let [r1, c1, r2, c2] = [Number(start.slice(1)), start[0], Number(end.slice(1)), end[0]];
for (let row = r1; row <= r2; row++) {
for (let col = c1.charCodeAt(0) - 'A'.charCodeAt(0); col <= c2.charCodeAt(0) - 'A'.charCodeAt(0); col++) {
total += this.sheet[row][col].val;
let cellPos = `${String.fromCharCode(65 + col)}${row+1}`;
this.sheet[r][c.charCodeAt(0) - 'A'.charCodeAt(0)].posCount[cellPos] = 1;
}
}
}
}
this.sheet[r][c.charCodeAt(0) - 'A'.charCodeAt(0)].val = total;
return total;
}
}
Another similar approach used except in JavaScript. Notice that the conversion between char and ASCII value is done using charCodeAt(0)
.
Java Solution
java
class Excel {
class Cell {
int val = 0;
Map<String, Integer> cells = new HashMap<>();
}
Cell[][] table;
public Excel(int H, char W) {
table = new Cell[H+1][W-'A'+1];
for(int i=0; i <= H; i++){
for(int j=0; j <= W-'A'; j++){
table[i][j] = new Cell();
}
}
}
public void set(int r, String c, int v) {
Cell cell = table[r][c.charAt(0)-'A'];
cell.val = v;
cell.cells.clear();
}
public int get(int r, String c) {
if(table[r][c.charAt(0)-'A'].cells.isEmpty()){
return table[r][c.charAt(0)-'A'].val;
} else {
return sum(r, c, new String[]{c});
}
}
public int sum(int r, String c, String[] strs) {
Cell cell = table[r][c.charAt(0)-'A'];
cell.cells.clear();
int total = 0;
for(String s : strs){
if(s.indexOf(":") < 0){
total += get(Integer.parseInt(s.substring(1)), s.substring(0,1));
cell.cells.put(s, cell.cells.getOrDefault(s, 0) + 1);
} else {
String[] range = s.split(":");
int rowStart = Integer.parseInt(range[0].substring(1));
char colStart = range[0].charAt(0);
int rowEnd = Integer.parseInt(range[1].substring(1));
char colEnd = range[1].charAt(0);
for(int i=rowStart; i<=rowEnd; i++){
for(char j=colStart; j<=colEnd; j++){
total += get(i, String.valueOf(j));
String key = j + String.valueOf(i);
cell.cells.put(key, cell.cells.getOrDefault(key, 0) + 1);
}
}
}
}
cell.val = total;
return total;
}
}
The Java solution uses a Cell
class similar to the previous solutions, with a val
attribute to store the value of the cell and a cells
HashMap to store any cells that contribute to its value. The set
and get
methods are straightforward, while the sum
method involves more complex logic to deal with ranges. If a range is provided, the range is parsed and a nested loop is used to loop through the cells in the range, while updating the total and the cell HashMap as needed. The final total sum is then stored in the cell and returned.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorHow does quick sort divide the problem into subproblems?
Recommended Readings
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!