1. Decode the hidden message in a two-dimensional character grid.
Implement decodeMessage(grid) in JavaScript. grid is a rectangular array whose entries are one-character strings; it may contain zero rows, and the function must not mutate it. When a cell exists, start at the top-left cell, append that character, and move one row down and one column right. At the bottom boundary, switch to moving one row up and one column right; at the top boundary, switch back to down-right. Continue alternating at vertical boundaries and stop when neither diagonal move is possible. Return the collected string, or '' when the grid has no character. Inputs outside this reported rectangular-character schema are out of scope. Example: decodeMessage([['I','B','C','A','L','K','A'],['D','R','F','C','A','E','A'],['G','H','O','E','L','A','D']]) must return 'IROCLED'. The traversal should visit at most one cell per column, using O(1) auxiliary state besides the returned string.
I would simulate the diagonal path with three small pieces of state: the current row, the current column, and a vertical direction. I start at the top-left cell and append its character. Each successful move goes one column right. If the next row crosses the top or bottom boundary, I reverse the vertical direction and try again. I stop when no valid diagonal move exists. This visits at most one cell per column, so it takes O(C) time and O(1) auxiliary space besides the returned string.
See the Code while reading this explanation.
The grid is a rectangle of characters. We start with the character in the top-left corner. Then we keep moving diagonally to the right. We go down until the next step would leave the bottom, then we go up. At the top, we switch back to going down. Every visited character is added to the message. We stop when there is no valid diagonal cell in the next column. The input must stay unchanged. For the given example, the visited characters spell "IROCLED".
- Can the grid have zero rows or rows with zero columns?
- Can I assume every non-empty row has the same number of columns and every entry is exactly one character?
- Should the function leave the input grid unchanged?
The input is a rectangular two-dimensional array of one-character strings. The function must not change it. If the grid has no character, return ''. Otherwise, start at row 0, column 0, collect characters in the required diagonal order, and return the collected string.
Use r for the current row and c for the current column. Use dr for the vertical direction. dr = 1 means down-right and dr = -1 means up-right. Start with r = 0, c = 0, dr = 1, and result = ''.
Append grid[r][c] to result. If c + 1 is outside the grid, stop because there is no next column. Otherwise, calculate nextR = r + dr. If nextR is above the top or below the bottom, reverse dr and calculate nextR again. If that second row is also invalid, stop. Otherwise, move to (nextR, c + 1).
The grid has 3 rows and 7 columns. The visited cells are (0,0) → (1,1) → (2,2) → (1,3) → (0,4) → (1,5) → (2,6). Their characters are I → R → O → C → L → E → D. At (2,2), the down-right move would cross the bottom, so the direction changes to up-right. At (0,4), the up-right move would cross the top, so the direction changes back to down-right. At (2,6), there is no next column, so the algorithm stops and returns "IROCLED".
The invariant is simple: after each successful move, the column increases by exactly one and the row follows the currently valid diagonal direction. The direction changes only when the next row would cross a vertical boundary. Because the column never moves left, no cell is revisited. Therefore the algorithm follows exactly the required path and appends the required characters in order.
The code first handles an empty grid or an empty first row. It stores the row and column counts. Then it runs the traversal with r, c, and dr. Each loop appends the current character, checks whether another column exists, calculates the next row, flips direction when needed, and moves to the next valid cell. Finally, it returns the collected string.
Let C be the number of columns. At most one cell is visited in each column, so the time complexity is O(C). The algorithm keeps only a few variables, so auxiliary space is O(1) besides the returned string. An empty grid returns ''. A one-row grid returns only the top-left character because both diagonal row choices are invalid. A one-column grid also returns only its first character.
Use direct simulation. Keep the current row, current column, and vertical direction. The central invariant is that every successful move advances exactly one column and uses the valid diagonal direction for that position. Try nextR = r + dr. If that row crosses the top or bottom boundary, reverse dr and try again. If the recomputed row is still invalid, no diagonal move exists and the traversal stops. Because the column only increases, the algorithm never revisits a cell and processes at most one cell per column.
function decodeMessage(grid) {
// No character exists when there are no rows or the first row is empty.
if (grid.length === 0 || grid[0].length === 0) return '';
// Store the rectangular grid dimensions for boundary checks.
const rows = grid.length;
const cols = grid[0].length;
// Start at the top-left cell.
let r = 0;
let c = 0;
// dr = 1 means down-right. dr = -1 means up-right.
let dr = 1;
// Build the decoded message without mutating the grid.
let result = '';
while (true) {
// Collect the character at the current visited cell.
result += grid[r][c];
// A diagonal move always needs the next column.
if (c + 1 >= cols) break;
// First try to keep moving in the current vertical direction.
let nextR = r + dr;
// If that row crosses a vertical boundary, reverse direction.
if (nextR < 0 || nextR >= rows) {
dr = -dr;
nextR = r + dr;
}
// If the opposite diagonal is also invalid, no move is possible.
if (nextR < 0 || nextR >= rows) break;
// Move to the valid diagonal cell in the next column.
r = nextR;
c += 1;
}
// Return the characters in the exact order they were visited.
return result;
}
// Same example as the diagram.
const grid = [
['I', 'B', 'C', 'A', 'L', 'K', 'A'],
['D', 'R', 'F', 'C', 'A', 'E', 'A'],
['G', 'H', 'O', 'E', 'L', 'A', 'D'],
];
console.log(decodeMessage(grid)); // IROCLEDLet C be the number of columns. The traversal visits at most one cell in each column, so the time complexity is O(C). It does not create another grid, map, set, stack, or queue. It only keeps a few variables such as the current row, current column, direction, and next row. Therefore the auxiliary space is O(1) besides the returned string. The returned string itself can contain up to C characters because those characters are the required output.
This kind of direct grid simulation is useful when software must follow a fixed movement rule through a matrix. Similar patterns appear in board and puzzle logic, image-grid processing, animation paths, and other tasks where a position and direction fully describe the current state.
This problem checks whether you can turn movement rules into correct state updates. The interviewer is looking for careful boundary handling, a clear invariant, and a correct stopping condition. It also tests whether your code and explanation stay consistent, whether you notice special cases such as a one-row grid, whether you avoid unnecessary data structures, and whether you can justify O(C) time and O(1) auxiliary space for the exact traversal.
- Moving outside the grid first and only then trying to repair the position. The code should validate the next row before updating
r. - Forgetting that every successful move must also increase the column by one.
- Stopping after the first invalid diagonal instead of reversing the vertical direction and trying the opposite diagonal.
- Mishandling a one-row grid, where both diagonal row choices are invalid after the first character.
- Claiming O(R × C) time even though the traversal visits at most one cell per column.
Explain the movement with one sentence: the column always moves right, while dr controls whether the row moves down or up. Then show that dr flips only when the next row crosses the top or bottom boundary. This makes both the correctness argument and the O(C) time complexity easy to defend.









