L-Shaped String Transformation

Given a string s and a number of rows numRows, arrange the string in an L-shaped pattern from top to bottom, left to right. Then, read the characters row by row from left to right to produce a new string. Implement this transformation with the function signature:

transform(s: string, numRows: number) => string

For example, with input string "ABCDEFGHIJKLMNO" and 3 rows, the arrangement is as follows (using '-' as placeholders):

A--F--K
B--G--L
CDEHIJMNO

Output: "AFKBGLCDEHIJMNO"

With 4 rows:

A---H---O
B---I---
C---J---
DEFGKLMN

Output: "AHOBICJDFEGKLMN"

With 5 rows:

A----J
B----K
C----L
D----M
EFGHINO

Output: "AJBKCLDMEFGHINO"

Solution Approaches

Method 1: 2D Matrix Simulation

/**
 * Method 1: 2D Matrix Simulation
 * @param {string} inputStr
 * @param {number} totalRows
 * @return {string}
 */
const transformWithMatrix = (inputStr, totalRows) => {
  const strLength = inputStr.length;
  const rows = totalRows;

  if (rows === 1 || rows >= strLength) {
    return inputStr;
  }

  const period = rows * 2 - 1;
  const cols = Math.ceil(strLength / period) * rows;
  
  const grid = new Array(rows).fill(0).map(() => new Array(cols).fill(0));

  let currentRow = 0, currentCol = 0;
  
  for (let i = 0; i < strLength; ++i) {
    grid[currentRow][currentCol] = inputStr[i];
    
    if (i % period < rows - 1) {
      currentRow++;
    } else {
      currentCol++;
    }
    
    if ((i + 1) < strLength && (i + 1) % period === 0) {
      currentRow = 0;
    }
  }

  const result = [];
  for (const row of grid) {
    for (const char of row) {
      if (char !== 0) {
        result.push(char);
      }
    }
  }
  return result.join('');
};

Method 2: Optimized Matrix Representation

/**
 * Method 2: Optimized Matrix Representation
 * @param {string} inputStr
 * @param {number} totalRows
 * @return {string}
 */
const transformOptimized = (inputStr, totalRows) => {
  const strLength = inputStr.length;
  const rows = totalRows;

  if (rows === 1 || rows >= strLength) {
    return inputStr;
  }

  const rowBuffers = new Array(rows).fill(0).map(() => []);
  const period = rows * 2 - 1;
  
  let currentRow = 0;
  
  for (let i = 0; i < strLength; ++i) {
    rowBuffers[currentRow].push(inputStr[i]);
    
    if (i % period < rows - 1) {
      currentRow++;
    }
    
    if ((i + 1) < strLength && (i + 1) % period === 0) {
      currentRow = 0;
    }
  }

  return rowBuffers.flat().join('');
};

Method 3: Direct Construction

/**
 * Method 3: Direct Construction
 * @param {string} inputStr
 * @param {number} totalRows
 * @return {string}
 */
const transformDirect = (inputStr, totalRows) => {
  const strLength = inputStr.length;
  const rows = totalRows;

  if (rows === 1 || rows >= strLength) {
    return inputStr;
  }

  const period = rows * 2 - 1;
  const result = [];
  
  for (let rowIndex = 0; rowIndex < rows; rowIndex++) {
    for (let baseIndex = 0; baseIndex < strLength - rowIndex; baseIndex += period) {
      const charIndex = baseIndex + rowIndex;
      if (charIndex < strLength) {
        result.push(inputStr[charIndex]);
      }

      if (rowIndex === rows - 1) {
        for (let offset = rows - 1; offset > 0 && (baseIndex + period - offset) < strLength; --offset) {
          result.push(inputStr[baseIndex + period - offset]);
        }
      }
    }
  }
  
  return result.join('');
};

Visualization Aid

The matrix representation shows string indices:

0                 0+period             0+2*period
1                 1+period             1+2*period
2                 2+period             2+2*period
3  period-3 period-2 period-1  3+period  2*period-3 2*period-2 2*period-1 3+2*period

Tags: javascript string algorithm transformation pattern

Posted on Thu, 14 May 2026 21:23:58 +0000 by LDusan