1. 程式人生 > >[LeetCode] Design Excel Sum Formula 設計Excel表格求和公式

[LeetCode] Design Excel Sum Formula 設計Excel表格求和公式

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:

  1. You could assume that there won't be any circular sum reference. For example, A1 = sum(B1) and B1 = sum(A1).
  2. The test cases are using double-quotes to represent a character.
  3. 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;
};

參考資料: