package design; import java.util.*; /** * Created by gouthamvidyapradhan on 16/09/2017. * *

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. H is 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. * *

Solution: Build a graph and for each cell keep track of forward and backward links. When a * cell is updated with a new value broadcast the new value to all the forward links and remove all * the forward links pointing to this particular cell. */ public class Excel { private Map> fwdEdges; // Forward links from cell private Map> backEdge; // All backward links from cell private Map count; // Keep track of number of times the cell is linked private int[][] grid; // excel grid /** * Initialize datastructure * * @param H row * @param W column */ public Excel(int H, char W) { grid = new int[H][(Character.toUpperCase(W) - 'A') + 1]; fwdEdges = new HashMap<>(); backEdge = new HashMap<>(); count = new HashMap<>(); } public static void main(String[] args) throws Exception { Excel excel = new Excel(26, 'Z'); excel.set(1, 'A', 1); excel.set(1, 'I', 1); String[] arr = {"A1:D6", "A1:G3", "A1:C12"}; String[] arr1 = {"A1:D7", "D1:F10", "D3:I8", "I1:I9"}; System.out.println(excel.get(1, 'A')); System.out.println(excel.sum(7, 'D', arr)); System.out.println(excel.get(1, 'A')); System.out.println(excel.sum(10, 'G', arr1)); System.out.println(excel.get(1, 'A')); } /** * Set value to the grid * * @param r row * @param c column * @param v value */ public void set(int r, char c, int v) { setValue(r, c, v); removeForwardEdges(String.valueOf(c) + r); } private void setValue(int r, char c, int v) { int curr = grid[r - 1][Character.toUpperCase(c) - 'A']; grid[r - 1][Character.toUpperCase(c) - 'A'] = v; broadcast(v - curr, String.valueOf(c) + r); } /** * Remove all the links * * @param node node */ private void removeForwardEdges(String node) { List parents = backEdge.get(node); if (parents != null) { for (String p : parents) { Set children = fwdEdges.get(p); if (children != null) { count.remove(p + ":" + node); children.remove(node); } } } } /** * Broadcast to all the links * * @param v current node * @param node node */ private void broadcast(int v, String node) { Set children = fwdEdges.get(node); if (children != null) { for (String c : children) { int order = count.get(node + ":" + c); grid[Integer.parseInt(c.substring(1)) - 1][c.charAt(0) - 'A'] += (v * order); broadcast(v, c); } } } public int get(int r, char c) { return grid[r - 1][c - 'A']; } /** * Sum range of cells * * @param r row * @param c column * @param strs Strings * @return integer sum */ public int sum(int r, char c, String[] strs) { int sum = 0; // Remove all the forward and backward edges or links removeForwardEdges(c + String.valueOf(r)); backEdge.remove(c + String.valueOf(r)); Set nodes = new HashSet<>(); for (String s : strs) { String[] range = s.split(":"); if (range.length > 1) { int startRow = Integer.parseInt(range[0].substring(1)) - 1; int startColumn = range[0].charAt(0) - 'A'; int endRow = Integer.parseInt(range[1].substring(1)) - 1; int endColumn = range[1].charAt(0) - 'A'; for (int i = startRow; i <= endRow; i++) { for (int j = startColumn; j <= endColumn; j++) { char newC = (char) ('A' + j); nodes.add(newC + String.valueOf(i + 1)); sum += grid[i][j]; String key = newC + String.valueOf(i + 1) + ":" + (c + String.valueOf(r)); if (count.putIfAbsent(key, 1) != null) { count.put(key, (count.get(key) + 1)); } } } } else { sum += grid[Integer.parseInt(range[0].substring(1)) - 1][range[0].charAt(0) - 'A']; nodes.add(range[0]); String key = range[0] + ":" + (c + String.valueOf(r)); if (count.putIfAbsent(key, 1) != null) { count.put(key, count.get(key) + 1); } } } // set value setValue(r, c, sum); // make new forward-edges for (String n : nodes) { Set children = fwdEdges.get(n); if (children == null) { children = new HashSet<>(); fwdEdges.put(n, children); } children.add(c + String.valueOf(r)); } // make new back-edges List backEdges = new ArrayList<>(); backEdges.addAll(nodes); backEdge.put(c + String.valueOf(r), backEdges); return sum; } }