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:
- A
Stringthat contains the message to be displayed on the sign - An
intthat 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 sale, please come in
|
| 20 | 2 |
Everything on sale, please come in
|
| 40 | 1 | Everything on sale, please come in |
Method Specifications
numberOfLines()
- Returns an
intrepresenting the number of lines needed to display the text - In the examples above, would return 3, 2, and 1 respectively
getLines()
- Returns a
Stringcontaining the message broken into lines separated by semicolons (;) - Returns
nullif 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 = 3lines ✓ - For 4 characters with width 10:
(4 + 10 - 1) / 10 = 13 / 10 = 1line ✓ - For 6 characters with width 6:
(6 + 6 - 1) / 6 = 11 / 6 = 1line ✓
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:
- If message is empty, return
null - Initialize empty result String
- Get total number of lines by calling
numberOfLines() - Loop through each line (0 to numLines-1)
- Calculate starting index:
i * width - Calculate ending index:
Math.min(start + width, message.length())- This prevents going past the end of the message
- Extract substring and add to result
- Add semicolon after each line except the last one
String Manipulation Details:
-
substring(start, end)extracts characters from indexstart(inclusive) toend(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() / widthwithout 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()ingetLines()(could calculate separately, but less elegant)