Topics › Basics › Patterns

Pattern 16

Easy TUF ID 408 A2Z

Problem

Given a positive integer n, print the alpha-ramp: n rows where row i (counting from 1) prints the i-th capital letter exactly i times, separated by single spaces. Row 1 is A, row 2 is B B, row 3 is C C C, and so on.

Constraints: 1 ≤ n ≤ 26, because there are 26 letters.

Example 1

n = 3
A
B B
C C C

Row i uses only one letter, the i-th one, repeated i times.

Example 2

n = 5
A
B B
C C C
D D D D
E E E E E

The letter changes with the row, not with the column.

Reading the pattern

Compare this with the increasing letter triangle A / A B / A B C. The shape is identical (i items in row i), but the content rule is flipped: there the letter depended on the column, here it depends on the row. Row i prints (char) ('A' + i - 1), and the inner loop merely repeats it.

That is the whole lesson of this pattern: before writing loops, decide which index each piece of content depends on. If it depends on the row only, compute it once per row, outside the inner loop. That is both faster to write and less error-prone than recomputing inside.

Because every row consists of one symbol repeated with a separator, there is also a one-line way to produce a row with the String.repeat method that Java has had since version 11. Both approaches below return the finished picture as a single String (newline after every row) so main can compare them with expected strings before printing.

Approach 1: Nested loops, letter fixed per row

Intuition. Outer loop over rows, inner loop over columns. Compute the row’s letter once with char arithmetic, then let the inner loop append it i times with a space before every copy except the first.

Algorithm.

  1. For i from 1 to n: let ch = (char) ('A' + i - 1).
  2. For j from 0 to i - 1: if j > 0 append a space; append ch.
  3. End the row with a newline.
static String nestedLoops(int n) {
    StringBuilder sb = new StringBuilder();
    for (int i = 1; i <= n; i++) {
        char ch = (char) ('A' + i - 1);
        for (int j = 0; j < i; j++) {
            if (j > 0) sb.append(' ');
            sb.append(ch);
        }
        sb.append('\n');
    }
    return sb.toString();
}

Complexity. Time O(n^2): n(n+1)/2 letters and n(n-1)/2 spaces, each appended in O(1). Space O(n^2) for the output string; no other memory depends on n.

The cast (char) ('A' + i - 1) is evaluated once per row. Note the parentheses: 'A' + i - 1 is an int, and the cast applies to the whole expression. Writing (char) 'A' + i - 1 would cast 'A' alone (a no-op) and leave you with an int, which append would print as a number.

Approach 2: One repeat call per row

Intuition. (ch + " ") is the two-character string "C ". Repeating it i times gives "C C C ", and stripTrailing() removes the one trailing space. That expresses “this letter, i times, separated by spaces” in a single expression, with no inner loop to get wrong.

The ch + " " part deserves a second look. ch is a char and " " is a String; because one operand is a String, Java performs string concatenation and produces "C ". Had both operands been chars (ch + ' ') the result would have been an int sum of code points.

Algorithm.

  1. For i from 1 to n: let ch = (char) ('A' + i - 1).
  2. Append (ch + " ").repeat(i).stripTrailing() and a newline.
static String withRepeat(int n) {
    StringBuilder sb = new StringBuilder();
    for (int i = 1; i <= n; i++) {
        char ch = (char) ('A' + i - 1);
        sb.append((ch + " ").repeat(i).stripTrailing()).append('\n');
    }
    return sb.toString();
}

Dry run for n = 4:

i ch (ch + " ").repeat(i) after stripTrailing
1 A "A " A
2 B "B B " B B
3 C "C C C " C C C
4 D "D D D D " D D D D

Complexity. Time O(n^2): repeat(i) produces 2i characters and stripTrailing scans back over at most one of them, so each row costs O(i). Space O(n^2) for the output plus O(n) for the temporary row strings, which are garbage after each iteration.

Is this better than Approach 1? Not asymptotically. It is shorter and reads as a description of the row, which matters when the pattern is one piece of a bigger program. The loop version is what you write when you need to control each character, for instance to vary the separator. Knowing both lets you choose.

Edge cases and pitfalls