2023 AP® Computer Science A FRQ 2 — Sign (Class Design)

2023 AP® Computer Science A FRQ 2 — Sign (Class Design)

Topic: Complete class implementation, String manipulation, integer division
2025 Curriculum Alignment: Unit 3 (Class Creation) + Unit 2 (String methods)


Question

This question involves methods that distribute text across lines of an electronic sign. The electronic sign and the text to be displayed on it are represented by the Sign class. You will write the complete Sign class, which contains a constructor and two methods.

Class Specifications

The Sign class constructor has two parameters:

  1. A String that contains the message to be displayed on the sign
  2. An int that contains the width of each line of the sign (the positive maximum number of characters per line)

A sign contains as many lines as are necessary to display the entire message. The message is split among the lines of the sign without regard to spaces or punctuation. Only the last line of the sign may contain fewer characters than the width indicated by the constructor parameter.

Examples: The message "Everything on sale, please come in" (34 characters) displayed on signs of different widths:

Width Lines Display
15 3 Everything on s
ale, please com
e in
20 2 Everything on sale,
please come in
40 1 Everything on sale, please come in

Method Specifications

numberOfLines()

  • Returns an int representing the number of lines needed to display the text
  • In the examples above, would return 3, 2, and 1 respectively

getLines()

  • Returns a String containing the message broken into lines separated by semicolons (;)
  • Returns null if the message is the empty string
  • The constructor parameter will not include any semicolons
  • No semicolon should appear at the end of the returned String

Sample Execution

Statement Return Value Explanation
Sign sign1 = new Sign("ABC222DE", 3); (none) Message has 8 characters, sign width is 3
x = sign1.numberOfLines(); 3 Needs 3 lines for 8 characters with width 3
str = sign1.getLines(); "ABC;222;DE" Semicolons separate the three lines
Sign sign2 = new Sign("ABCD", 10); (none) Message has 4 characters, sign width is 10
x = sign2.numberOfLines(); 1 Needs 1 line for 4 characters with width 10
str = sign2.getLines(); "ABCD" No semicolon (fits on one line)
Sign sign4 = new Sign("", 4); (none) Empty message
x = sign4.numberOfLines(); 0 No text to display
str = sign4.getLines(); null No text to display
Sign sign5 = new Sign("AB_CD_EF", 2); (none) Message has 8 characters, sign width is 2
x = sign5.numberOfLines(); 4 Needs 4 lines for 8 characters with width 2
str = sign5.getLines(); "AB;_C;D_;EF" Semicolons separate the four lines

Write the complete Sign class. Your implementation must meet all specifications and conform to the examples shown above.


Solution & Explanation

Complete Sign Class

public class Sign {
    private String message;
    private int width;
    
    public Sign(String message, int width) {
        this.message = message;
        this.width = width;
    }
    
    public int numberOfLines() {
        if (message.length() == 0) {
            return 0;
        }
        
        return (message.length() + width - 1) / width;
    }
    
    public String getLines() {
        if (message.length() == 0) {
            return null;
        }
        
        String result = "";
        int numLines = numberOfLines();
        
        for (int i = 0; i < numLines; i++) {
            int start = i * width;
            int end = Math.min(start + width, message.length());
            
            result += message.substring(start, end);
            
            if (i < numLines - 1) {
                result += ";";
            }
        }
        
        return result;
    }
}

Detailed Explanation

Instance Variables

private String message;
private int width;

Store the message and the width of each line. These are private because they shouldn't be directly accessible from outside the class.

Constructor

public Sign(String message, int width) {
    this.message = message;
    this.width = width;
}

Initializes the instance variables with the provided parameters. Uses this keyword to distinguish between instance variables and parameters.

numberOfLines() Method

public int numberOfLines() {
    if (message.length() == 0) {
        return 0;
    }
    
    return (message.length() + width - 1) / width;
}

Logic:

  • If message is empty, return 0
  • Otherwise, calculate: (message.length() + width - 1) / width
  • This formula does "ceiling division" (rounds up) without using floating-point math

Why the formula works:

  • For 8 characters with width 3: (8 + 3 - 1) / 3 = 10 / 3 = 3 lines ✓
  • For 4 characters with width 10: (4 + 10 - 1) / 10 = 13 / 10 = 1 line ✓
  • For 6 characters with width 6: (6 + 6 - 1) / 6 = 11 / 6 = 1 line ✓

Alternative: You could also use (int) Math.ceil((double) message.length() / width), but the formula above is more elegant.

getLines() Method

public String getLines() {
    if (message.length() == 0) {
        return null;
    }
    
    String result = "";
    int numLines = numberOfLines();
    
    for (int i = 0; i < numLines; i++) {
        int start = i * width;
        int end = Math.min(start + width, message.length());
        
        result += message.substring(start, end);
        
        if (i < numLines - 1) {
            result += ";";
        }
    }
    
    return result;
}

Logic:

  1. If message is empty, return null
  2. Initialize empty result String
  3. Get total number of lines by calling numberOfLines()
  4. Loop through each line (0 to numLines-1)
  5. Calculate starting index: i * width
  6. Calculate ending index: Math.min(start + width, message.length())
    • This prevents going past the end of the message
  7. Extract substring and add to result
  8. Add semicolon after each line except the last one

String Manipulation Details:

  • substring(start, end) extracts characters from index start (inclusive) to end (exclusive)
  • Math.min() ensures we don't try to extract beyond the message length
  • The condition if (i < numLines - 1) adds semicolons only between lines, not after the last line

Example Walkthrough

Example: Sign sign1 = new Sign("ABC222DE", 3);

i start end substring result so far
0 0 3 "ABC" "ABC;"
1 3 6 "222" "ABC;222;"
2 6 8 (min of 9 and 8) "DE" "ABC;222;DE"

Final result: "ABC;222;DE"


Scoring Notes

Total: 9 points

Class declaration and instance variables (1 point):

  • Declares class with appropriate header
  • Includes instance variables for message and width

Constructor (2 points):

  • +1: Proper constructor header with correct parameters
  • +1: Initializes instance variables correctly

numberOfLines() (2 points):

  • +1: Returns 0 for empty message
  • +1: Correctly calculates number of lines (ceiling division)

getLines() (4 points):

  • +1: Returns null for empty message
  • +1: Uses loop to process all lines
  • +1: Correctly extracts substrings for each line
  • +1: Adds semicolons between lines (but not after last line)

Common Mistakes to Avoid:

  • Forgetting to handle empty string (should return 0 and null respectively)
  • Adding a semicolon after the last line in getLines()
  • Using message.length() / width without ceiling (would give wrong answer for non-exact divisions)
  • Going out of bounds in substring() for the last line
  • Not declaring instance variables as private
  • Forgetting to call numberOfLines() in getLines() (could calculate separately, but less elegant)

College Board Resources

Contact form