Facebook Pixel

2194. Cells in a Range on an Excel Sheet

EasyString
Leetcode Link

Problem Description

You are given a string that represents a rectangular range of cells in an Excel sheet. The string follows the format "<col1><row1>:<col2><row2>", where:

  • The first part <col1><row1> represents the top-left cell
  • The second part <col2><row2> represents the bottom-right cell
  • These two parts are separated by a colon :

In Excel notation:

  • Columns are represented by letters ('A' for column 1, 'B' for column 2, 'C' for column 3, and so on)
  • Rows are represented by numbers (1, 2, 3, ...)

For example, "A1:C3" represents a 3×3 grid from cell A1 to cell C3.

Your task is to return a list of all cells within this rectangular range (inclusive of boundaries). The cells should be:

  1. Represented as strings in Excel format (like "A1", "B2", etc.)
  2. Sorted first by column (A before B before C) and then by row (1 before 2 before 3)

The solution iterates through the range by:

  1. Extracting the start column s[0] and end column s[-2] (the third character from the end)
  2. Extracting the start row s[1] and end row s[-1] (the last character)
  3. Using nested loops to generate all cells, with the outer loop for columns and inner loop for rows
  4. Converting column indices back to letters using chr() and ord() functions

For instance, if the input is "K1:L2", the output would be ["K1", "K2", "L1", "L2"].

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that we need to generate all cells in a rectangular grid. Think of it like filling in all the squares on a checkerboard within a given rectangle.

Since the input format is fixed ("<col1><row1>:<col2><row2>"), we can extract the boundaries directly:

  • The first character is the starting column
  • The second character is the starting row
  • The second-to-last character is the ending column
  • The last character is the ending row

To generate all cells systematically, we need to iterate through every combination of columns and rows within our boundaries. This naturally suggests using nested loops.

The ordering requirement (sorted by column first, then by row) tells us which loop should be outer and which should be inner. Since we want all rows of column A before moving to column B, we make columns the outer loop and rows the inner loop.

For working with column letters, we leverage ASCII values. In ASCII, letters are consecutive numbers ('A' = 65, 'B' = 66, etc.), so we can:

  • Convert a letter to its ASCII value with ord()
  • Iterate through consecutive numbers
  • Convert back to letters with chr()

This approach is straightforward because Excel's column naming (A, B, C...) maps directly to consecutive ASCII values, making the conversion trivial. The row numbers are already integers in the string, so we just need to extract and iterate through them.

The beauty of this solution is its simplicity - we're essentially just enumerating all points in a rectangle, taking advantage of Python's list comprehension to make the code concise and readable.

Solution Approach

The solution uses a straightforward simulation approach with list comprehension to generate all cells in the specified range.

Step-by-step implementation:

  1. Parse the input string boundaries:

    • Starting column: s[0] (first character)
    • Starting row: s[1] (second character)
    • Ending column: s[-2] (second-to-last character, skipping the last row digit)
    • Ending row: s[-1] (last character)
  2. Generate the column range:

    • Convert the starting column letter to ASCII using ord(s[0])
    • Convert the ending column letter to ASCII using ord(s[-2])
    • Create a range from start to end (inclusive) by adding 1: range(ord(s[0]), ord(s[-2]) + 1)
  3. Generate the row range:

    • Convert the starting row from string to integer: int(s[1])
    • Convert the ending row from string to integer: int(s[-1])
    • Create a range from start to end (inclusive): range(int(s[1]), int(s[-1]) + 1)
  4. Build each cell string:

    • For each column index i, convert back to letter using chr(i)
    • For each row index j, convert to string using str(j)
    • Concatenate them: chr(i) + str(j)
  5. Use nested iteration with list comprehension:

    [chr(i) + str(j) 
     for i in range(ord(s[0]), ord(s[-2]) + 1)
     for j in range(int(s[1]), int(s[-1]) + 1)]

    The outer loop iterates through columns, and the inner loop iterates through rows, ensuring the correct sorting order (column-major).

Example walkthrough with "K1:L2":

  • Column range: ord('K') to ord('L') → 75 to 76
  • Row range: 1 to 2
  • Generation order:
    • i=75, j=1chr(75) + '1'"K1"
    • i=75, j=2chr(75) + '2'"K2"
    • i=76, j=1chr(76) + '1'"L1"
    • i=76, j=2chr(76) + '2'"L2"
  • Result: ["K1", "K2", "L1", "L2"]

The time complexity is O(m × n) where m is the number of columns and n is the number of rows in the range, as we must generate each cell. The space complexity is also O(m × n) for storing the result list.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with the input "B2:D4".

Step 1: Parse the boundaries

  • Starting column: s[0] = 'B'
  • Starting row: s[1] = '2'
  • Ending column: s[-2] = 'D' (second-to-last character)
  • Ending row: s[-1] = '4' (last character)

Step 2: Convert columns to ASCII values

  • ord('B') = 66
  • ord('D') = 68
  • Column range: 66 to 68 (inclusive)

Step 3: Convert rows to integers

  • int('2') = 2
  • int('4') = 4
  • Row range: 2 to 4 (inclusive)

Step 4: Generate all cells using nested loops

The list comprehension executes in this order:

i = 66 (column B):
  j = 2: chr(66) + '2' = 'B' + '2' = "B2"
  j = 3: chr(66) + '3' = 'B' + '3' = "B3"
  j = 4: chr(66) + '4' = 'B' + '4' = "B4"

i = 67 (column C):
  j = 2: chr(67) + '2' = 'C' + '2' = "C2"
  j = 3: chr(67) + '3' = 'C' + '3' = "C3"
  j = 4: chr(67) + '4' = 'C' + '4' = "C4"

i = 68 (column D):
  j = 2: chr(68) + '2' = 'D' + '2' = "D2"
  j = 3: chr(68) + '3' = 'D' + '3' = "D3"
  j = 4: chr(68) + '4' = 'D' + '4' = "D4"

Final Result: ["B2", "B3", "B4", "C2", "C3", "C4", "D2", "D3", "D4"]

Notice how the cells are naturally sorted by column first (all B's, then all C's, then all D's) and within each column by row number (2, 3, 4). This ordering is achieved by making columns the outer loop and rows the inner loop in our list comprehension.

Solution Implementation

1class Solution:
2    def cellsInRange(self, s: str) -> List[str]:
3        """
4        Generate all cells in a rectangular range on a spreadsheet.
5      
6        Args:
7            s: A string in format "C1:R2" representing the range from top-left to bottom-right cell
8      
9        Returns:
10            A list of strings representing all cells in the range in row-major order
11        """
12        # Extract start and end column characters
13        start_col = s[0]  # First character is the starting column
14        end_col = s[3]    # Fourth character (after ':') is the ending column
15      
16        # Extract start and end row numbers
17        start_row = int(s[1])  # Second character is the starting row number
18        end_row = int(s[4])    # Fifth character is the ending row number
19      
20        # Generate all cells in the range using list comprehension
21        result = []
22        for col_ascii in range(ord(start_col), ord(end_col) + 1):
23            for row_num in range(start_row, end_row + 1):
24                # Convert ASCII value back to character and concatenate with row number
25                cell = chr(col_ascii) + str(row_num)
26                result.append(cell)
27      
28        return result
29
1class Solution {
2    /**
3     * Returns all cells in a rectangular range on an Excel sheet.
4     * 
5     * @param s A string representing the range in format "C1R1:C2R2"
6     *          where C1, C2 are column letters and R1, R2 are row numbers
7     * @return List of all cells in the range in lexicographical order
8     */
9    public List<String> cellsInRange(String s) {
10        List<String> result = new ArrayList<>();
11      
12        // Extract range boundaries from input string
13        char startColumn = s.charAt(0);  // First column letter
14        char startRow = s.charAt(1);      // First row number
15        char endColumn = s.charAt(3);     // Last column letter (after ':')
16        char endRow = s.charAt(4);        // Last row number (after ':')
17      
18        // Iterate through all columns in the range
19        for (char column = startColumn; column <= endColumn; column++) {
20            // For each column, iterate through all rows in the range
21            for (char row = startRow; row <= endRow; row++) {
22                // Construct cell reference by concatenating column and row
23                String cell = "" + column + row;
24                result.add(cell);
25            }
26        }
27      
28        return result;
29    }
30}
31
1class Solution {
2public:
3    vector<string> cellsInRange(string s) {
4        // Result vector to store all cells in the range
5        vector<string> result;
6      
7        // Extract range boundaries from input string
8        // s[0] = start column (e.g., 'A')
9        // s[1] = start row (e.g., '1')
10        // s[3] = end column (e.g., 'C')
11        // s[4] = end row (e.g., '3')
12        char startColumn = s[0];
13        char startRow = s[1];
14        char endColumn = s[3];
15        char endRow = s[4];
16      
17        // Iterate through all columns from start to end (inclusive)
18        for (char currentColumn = startColumn; currentColumn <= endColumn; ++currentColumn) {
19            // For each column, iterate through all rows from start to end (inclusive)
20            for (char currentRow = startRow; currentRow <= endRow; ++currentRow) {
21                // Create cell reference string and add to result
22                // Using initializer list to construct string from two characters
23                result.push_back({currentColumn, currentRow});
24            }
25        }
26      
27        return result;
28    }
29};
30
1/**
2 * Returns all cells in a rectangular range defined by a string in Excel-like format
3 * @param s - Range string in format "C1:R3" where first part is top-left cell and second part is bottom-right cell
4 * @returns Array of all cell names within the specified range
5 */
6function cellsInRange(s: string): string[] {
7    // Array to store all cell names in the range
8    const result: string[] = [];
9  
10    // Extract column and row boundaries from the range string
11    // s[0] = start column letter, s[3] = end column letter
12    const startColumn: number = s.charCodeAt(0);
13    const endColumn: number = s.charCodeAt(3);
14  
15    // s[1] = start row number, s[4] = end row number
16    const startRow: number = s.charCodeAt(1);
17    const endRow: number = s.charCodeAt(4);
18  
19    // Iterate through all columns from start to end (inclusive)
20    for (let currentColumn: number = startColumn; currentColumn <= endColumn; currentColumn++) {
21        // For each column, iterate through all rows from start to end (inclusive)
22        for (let currentRow: number = startRow; currentRow <= endRow; currentRow++) {
23            // Convert ASCII codes back to characters and combine column letter with row number
24            const cellName: string = String.fromCharCode(currentColumn) + String.fromCharCode(currentRow);
25            result.push(cellName);
26        }
27    }
28  
29    return result;
30}
31

Time and Space Complexity

The time complexity is O(m × n), where m is the number of columns (letters) in the range and n is the number of rows (digits) in the range.

  • m = ord(s[-2]) - ord(s[0]) + 1 represents the range of columns from the first character to the third character in the input string
  • n = int(s[-1]) - int(s[1]) + 1 represents the range of rows from the second character to the fourth character in the input string
  • The nested list comprehension iterates through all m columns and for each column, iterates through all n rows, resulting in m × n total iterations

The space complexity is O(m × n) as the function creates and returns a list containing m × n strings, where each string represents a cell in the specified range.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Index Assumptions for Multi-Digit Row Numbers

The most critical pitfall in this solution is assuming fixed positions for parsing the input string. The code uses hardcoded indices like s[1], s[4], and s[3] to extract row and column information, which fails when row numbers have multiple digits.

Problem Example:

  • Input: "A1:B10"
  • The code incorrectly parses:
    • s[3] as ':' (not a column letter)
    • s[4] as 'B' (not a row number)
    • This causes errors or incorrect results

Solution:

class Solution:
    def cellsInRange(self, s: str) -> List[str]:
        # Split by colon to separate start and end cells
        start_cell, end_cell = s.split(':')
      
        # Parse start cell (column is first letter, rest is row number)
        start_col = start_cell[0]
        start_row = int(start_cell[1:])
      
        # Parse end cell (column is first letter, rest is row number)
        end_col = end_cell[0]
        end_row = int(end_cell[1:])
      
        result = []
        for col_ascii in range(ord(start_col), ord(end_col) + 1):
            for row_num in range(start_row, end_row + 1):
                cell = chr(col_ascii) + str(row_num)
                result.append(cell)
      
        return result

2. Assuming Single-Letter Columns Only

The code assumes columns are always single letters (A-Z), but Excel notation supports multi-letter columns (AA, AB, etc.) for sheets with more than 26 columns.

Problem Example:

  • Input: "AA1:AB2"
  • The current parsing logic would fail completely

Enhanced Solution for Multi-Letter Columns:

class Solution:
    def cellsInRange(self, s: str) -> List[str]:
        def parse_cell(cell_str):
            """Extract column letters and row number from a cell string"""
            i = 0
            while i < len(cell_str) and cell_str[i].isalpha():
                i += 1
            return cell_str[:i], int(cell_str[i:])
      
        def column_to_number(col_str):
            """Convert column letters to a number (A=1, B=2, ..., AA=27, etc.)"""
            result = 0
            for char in col_str:
                result = result * 26 + (ord(char) - ord('A') + 1)
            return result
      
        def number_to_column(num):
            """Convert a number back to column letters"""
            result = ""
            while num > 0:
                num -= 1
                result = chr(num % 26 + ord('A')) + result
                num //= 26
            return result
      
        # Split and parse cells
        start_cell, end_cell = s.split(':')
        start_col, start_row = parse_cell(start_cell)
        end_col, end_row = parse_cell(end_cell)
      
        # Convert columns to numbers for iteration
        start_col_num = column_to_number(start_col)
        end_col_num = column_to_number(end_col)
      
        # Generate all cells
        result = []
        for col_num in range(start_col_num, end_col_num + 1):
            col_str = number_to_column(col_num)
            for row_num in range(start_row, end_row + 1):
                result.append(col_str + str(row_num))
      
        return result

3. Not Validating Input Format

The code doesn't validate that the input follows the expected format, which could lead to runtime errors or unexpected behavior.

Solution with Input Validation:

class Solution:
    def cellsInRange(self, s: str) -> List[str]:
        # Validate input format
        if ':' not in s:
            raise ValueError("Input must contain ':' separator")
      
        parts = s.split(':')
        if len(parts) != 2:
            raise ValueError("Input must have exactly one ':' separator")
      
        # Continue with parsing...

These pitfalls highlight the importance of carefully analyzing input constraints and avoiding hardcoded assumptions about data format when parsing strings.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How would you design a stack which has a function min that returns the minimum element in the stack, in addition to push and pop? All push, pop, min should have running time O(1).


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More