Your task is to design the basic function of Excel and implement the function of sum formula. Specifically, you need to implement the following functions:
Excel(int H, char W):
This is the constructor. The inputs represents the height and width of the Excel form. His a positive integer, range from 1 to 26. It represents the height. W is a character range from 'A' to 'Z'. It represents that the width is the number of characters from 'A' to W. The Excel form content is represented by a height * width 2D integer array C
, it should be initialized to zero. You should assume that the first row of C
starts from 1, and the first column of C
starts from 'A'.
void Set(int row, char column, int val):
Change the value at C(row, column)
to be val.
int Get(int row, char column):
Return the value at C(row, column)
.
int Sum(int row, char column, List of Strings : numbers):
This function calculate and set the value at C(row, column)
, where the value should be the sum of cells represented by numbers
. This function return the sum result at C(row, column)
. This sum formula should exist until this cell is overlapped by another value or another sum formula.
numbers
is a list of strings that each string represent a cell or a range of cells. If the string represent a single cell, then it has the following format : ColRow
. For example, "F7" represents the cell at (7, F).
If the string represent a range of cells, then it has the following format : ColRow1:ColRow2
. The range will always be a rectangle, and ColRow1 represent the position of the top-left cell, and ColRow2 represents the position of the bottom-right cell.
Example 1:
Excel(3,"C"); // construct a 3*3 2D array with all zero. // A B C // 1 0 0 0 // 2 0 0 0 // 3 0 0 0 Set(1, "A", 2); // set C(1,"A") to be 2. // A B C // 1 2 0 0 // 2 0 0 0 // 3 0 0 0 Sum(3, "C", ["A1", "A1:B2"]); // set C(3,"C") to be the sum of value at C(1,"A") and the values sum of the rectangle range whose top-left cell is C(1,"A") and bottom-right cell is C(2,"B"). Return 4. // A B C // 1 2 0 0 // 2 0 0 0 // 3 0 0 4 Set(2, "B", 2); // set C(2,"B") to be 2. Note C(3, "C") should also be changed. // A B C // 1 2 0 0 // 2 0 2 0 // 3 0 0 6
Note:
- You could assume that there won't be any circular sum reference. For example, A1 = sum(B1) and B1 = sum(A1).
- The test cases are using double-quotes to represent a character.
- Please remember to RESET your class variables declared in class Excel, as static/class variables are persisted across multiple test cases. Please see here for more details.
這道題讓我們設計Excel表格的求和公式,Excel表格想必大家都用過,還是比較熟悉的,這里讓我們對單元格進行求和運算。由於這道題里要求二維數組的局部和,而且又會經常更新數組的值,博主第一反應覺得應該用之前那題Range Sum Query 2D - Mutable中的樹狀數組來做,結果哼哼哧哧的寫完后,發現下面這個test case沒通過:
["Excel","sum","set","get"]
[[3,"C"],[1,"A",["A2"]],[2,"A",1],[1,"A"]]
Expected:
[null,0,null,1]
仔細分析一下發現,這個case先把A2的值賦給了A1,此時A1和A2都是0,然后給A2賦值為1,求A1的值。大家的第一印象肯定是覺得A1還是0啊,其實在Excel中,相當於已經把A1和A2關聯起來了,只要A2點值發生了改變,A1的值也會跟着變,所以A1的值此時也為1。而樹狀數組的主要功能的優化區域和的計算速度,並沒有建立關聯的步驟,難怪不能通過OJ呢。這道題標記為Hard還是有道理的,我們要模擬出Excel表中的這種關聯方式,這里參考的是yupinglu大神的帖子,首先我們肯定需要一個二維數組mat來保存數據,然后需要一個map來建立單元格和區域和之間的映射,這里的區域和就是sum函數中的字符串數組表示的內容,可參見題目中的例子,有可能單個單元格或者多個。
我們來看set函數,如果我們改變了某個單元格的內容,那么如果作為結果單元格,那么對應的鏈接就會斷開。比如我們有三個單元格A1, B1, C1,我們設置的關聯是A1 + B1 = C1,那么我們改變A1和B1的值都是OK的,C1的值會自動更新。但如果我們改變了C1的值,那么這個關聯就不復存在了,Excel中也是這樣的。所以我們在改變某個單元格的時候,要將其的關聯刪除。
我們再來看get函數,我們在獲取某個單元格的值的時候,一定要先看其有沒有和其他單元格關聯,如果有的話,要重新計算一下關聯,有可能關聯的單元格的值已經發生改變了,那么當前作為結果單元格的值也需要改變;如果該單元格沒有任何關聯,那么就直接從數組mat中取值即可。
最后看本題的難點sum函數,要根據關聯格求出結果格的值,首先這個字符串數組可能有多個字符串,每個字符串有兩個可能,一種是單個的單元格,一種是兩個單元格中間用冒號隔開。那么我們需要分情況討論,區別這兩種情況的方法就是看冒號是否存在,如果不存在,就說明只有一個單元格,我們將其數字和字母都提取出來,調用get函數,將該位置的值加入結果res中;如果冒號存在,我們根據冒號的位置,分別將兩個單元格的字母和數字提取出來,然后遍歷這兩個單元格之間所有的單元格,調用get函數並將返回值加入結果res中。這個遍歷相加的過程可能可以用樹狀數組來優化,但由於這不是此題的考察重點,所以直接遍歷就OK。最后別忘了建立目標單元格和區域字符串數組之間的映射,並返回結果res即可。
class Excel { public: Excel(int H, char W) { m.clear(); mat.resize(H, vector<int>(W - 'A', 0)); } void set(int r, char c, int v) { if (m.count({r, c})) m.erase({r, c}); mat[r - 1][c - 'A'] = v; } int get(int r, char c) { if (m.count({r, c})) return sum(r, c, m[{r, c}]); return mat[r - 1][c - 'A']; } int sum(int r, char c, vector<string> strs) { int res = 0; for (string str : strs) { auto found = str.find_last_of(":"); if (found == string::npos) { char y = str[0]; int x = stoi(str.substr(1)); res += get(x, y); } else { int x1 = stoi(str.substr(1, (int)found - 1)), y1 = str[0] - 'A'; int x2 = stoi(str.substr(found + 2)), y2 = str[found + 1] - 'A'; for (int i = x1; i <= x2; ++i) { for (int j = y1; j <= y2; ++j) { res += get(i, j + 'A'); } } } } m[{r, c}] = strs; return res; } private: vector<vector<int>> mat; map<pair<int, char>, vector<string>> m; };
參考資料:
https://discuss.leetcode.com/topic/93819/c-3-ms-solution-easy-to-understand
https://discuss.leetcode.com/topic/93812/c-3-ms-concise-and-easy-to-understand