CF 5B - Center Alignment
We are given several lines of text. Every line may contain letters, digits, and spaces inside the line, but never at the beginning or end. The task is to print all lines inside a rectangular frame made of * characters.
Rating: 1200
Tags: implementation, strings
Solve time: 1m 24s
Verified: yes
Solution
Problem Understanding
We are given several lines of text. Every line may contain letters, digits, and spaces inside the line, but never at the beginning or end. The task is to print all lines inside a rectangular frame made of * characters.
The inside width of the frame must equal the length of the longest line. Every shorter line has to be centered within that width. If the remaining empty space is odd, perfect centering is impossible because one side must receive one more space than the other. The problem asks us to alternate that extra space between the left and right sides, starting with the left side getting fewer spaces first.
For example, if the maximum width is 10 and a line has length 7, then there are 3 extra spaces to distribute. One side gets 1, the other gets 2. The first such line should become closer to the left edge, meaning left padding 1 and right padding 2. The next odd case should flip and become closer to the right edge.
The constraints are tiny. The total number of characters across all lines is at most 1000, so even relatively inefficient string operations are safe. We can freely scan all lines multiple times without worrying about performance. A straightforward implementation is already fast enough.
The tricky part is not speed, it is formatting correctness. Several edge cases can silently break a careless implementation.
One subtle case is completely empty lines. Consider:
abc
d
The empty line still has to appear inside the frame:
*****
*abc*
* *
* d *
*****
A common mistake is skipping empty input lines while reading.
Another tricky case is alternating the extra space for odd differences. Suppose the longest line has width 6:
abcdef
abc
xy
The line "abc" needs 3 extra spaces. The first odd case should use left padding 1 and right padding 2:
* abc *
The next odd case must flip:
* xy *
If we always put the larger side on the right, the output becomes incorrect.
There is also the corner case where all lines already have the same length:
abc
def
ghi
No padding is needed beyond the frame itself:
*****
*abc*
*def*
*ghi*
*****
A buggy implementation might accidentally insert unnecessary spaces.
Approaches
The most direct approach is to try every possible left-right padding split for every line until we find a valid centered arrangement. For a line of length k inside width W, we can test every pair (L, R) such that L + R = W - k and |L - R| <= 1. This works because the definition of centered text allows only those distributions.
Even though this brute-force idea is unnecessary, it is still fast enough here. The maximum width is at most 1000, so trying all padding splits for every line costs at most around one million operations.
The problem becomes much cleaner once we observe that the padding is almost completely determined. If the remaining space is even, both sides must receive exactly the same number of spaces. If the remaining space is odd, there are only two valid choices:
left = diff // 2
right = diff // 2 + 1
or
left = diff // 2 + 1
right = diff // 2
The statement explicitly tells us which one to use each time: alternate between them, starting with the line being closer to the left side.
This observation removes all searching. We only need:
- Read all lines.
- Find the maximum length.
- For each line, compute the missing spaces.
- Distribute them evenly, alternating odd cases.
- Print the frame.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(n * W) | O(n) | Accepted |
| Optimal | O(total characters) | O(n) | Accepted |
Here, n is the number of lines and W is the maximum width.
Algorithm Walkthrough
- Read every input line until EOF and remove only the trailing newline character.
We must preserve internal spaces and empty lines. Using rstrip('\n') is important because a plain strip() would destroy meaningful spaces.
2. Compute the maximum line length.
This determines the inside width of the frame. 3. Print the top border.
The border width equals max_len + 2 because the frame adds one * on each side.
4. Process each line independently.
Let diff = max_len - len(line) be the number of spaces we still need to insert.
5. If diff is even, split it equally.
Use:
left = right = diff // 2
This gives perfect centering.
6. If diff is odd, alternate the larger side.
Maintain a boolean flag. For the first odd case:
left = diff // 2
right = diff // 2 + 1
For the next odd case:
left = diff // 2 + 1
right = diff // 2
Then flip the flag. 7. Print:
* + left spaces + line + right spaces + *
- After all lines, print the bottom border.
Why it works
Every line must occupy exactly max_len characters inside the frame. The algorithm always adds exactly diff spaces, so the final width is correct.
When diff is even, equal padding is the only centered arrangement possible. When diff is odd, the only valid centered arrangements differ by one space between sides. The alternating flag follows the exact tie-breaking rule from the statement, so every ambiguous case is resolved correctly.
Because the top and bottom borders both use width max_len + 2, the frame fully surrounds every formatted line.
Python Solution
import sys
input = sys.stdin.readline
lines = [line.rstrip('\n') for line in sys.stdin]
max_len = max(len(line) for line in lines)
border = '*' * (max_len + 2)
print(border)
left_turn = True
for line in lines:
diff = max_len - len(line)
if diff % 2 == 0:
left = right = diff // 2
else:
if left_turn:
left = diff // 2
right = diff // 2 + 1
else:
left = diff // 2 + 1
right = diff // 2
left_turn = not left_turn
print('*' + ' ' * left + line + ' ' * right + '*')
print(border)
The first part reads every line exactly as written. Using rstrip('\n') instead of strip() matters because the problem allows spaces inside lines, and removing them would corrupt the text.
The maximum line length defines the width of the text area. Every formatted line must match this width before adding the border characters.
The variable left_turn controls the alternating behavior for odd padding differences. It flips only when the difference is odd. Even differences have only one valid split, so alternating does not apply there.
The expressions:
diff // 2
and
diff // 2 + 1
correctly distribute odd differences because integer division rounds down.
The border length is max_len + 2 because the frame contributes one * on each side of the text area.
Worked Examples
Example 1
Input:
This is
Codeforces
Beta
Round
5
Maximum length is 10.
| Line | Length | diff | Left spaces | Right spaces | Result |
|---|---|---|---|---|---|
This is |
9 | 1 | 0 | 1 | *This is * |
| `` | 0 | 10 | 5 | 5 | * * |
Codeforces |
10 | 0 | 0 | 0 | *Codeforces* |
Beta |
4 | 6 | 3 | 3 | * Beta * |
Round |
5 | 5 | 2 | 3 | * Round * |
5 |
1 | 9 | 5 | 4 | * 5 * |
Final output:
************
*This is *
* *
*Codeforces*
* Beta *
* Round *
* 5 *
************
This trace demonstrates the alternating rule. "Round" and "5" both require odd padding, and the larger side flips between right and left.
Example 2
Input:
abcdef
abc
xy
Maximum length is 6.
| Line | Length | diff | Left spaces | Right spaces | Result |
|---|---|---|---|---|---|
abcdef |
6 | 0 | 0 | 0 | *abcdef* |
abc |
3 | 3 | 1 | 2 | * abc * |
xy |
2 | 4 | 2 | 2 | * xy * |
Final output:
********
*abcdef*
* abc *
* xy *
********
This example shows that even differences always split evenly, while odd differences use the alternating tie-break rule.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(total characters) | Each line is scanned and printed once |
| Space | O(n) | Stores all input lines |
The total number of characters is at most 1000, so the solution runs comfortably within the limits. Even Python string operations are effectively instantaneous for this input size.
Test Cases
# helper: run solution on input string, return output string
import sys, io
def solve():
import sys
input = sys.stdin.readline
lines = [line.rstrip('\n') for line in sys.stdin]
max_len = max(len(line) for line in lines)
border = '*' * (max_len + 2)
out = [border]
left_turn = True
for line in lines:
diff = max_len - len(line)
if diff % 2 == 0:
left = right = diff // 2
else:
if left_turn:
left = diff // 2
right = diff // 2 + 1
else:
left = diff // 2 + 1
right = diff // 2
left_turn = not left_turn
out.append('*' + ' ' * left + line + ' ' * right + '*')
out.append(border)
print('\n'.join(out))
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
sys.stdout = io.StringIO()
solve()
return sys.stdout.getvalue()
# provided sample
assert run(
"""This is
Codeforces
Beta
Round
5
"""
) == (
"""************
*This is *
* *
*Codeforces*
* Beta *
* Round *
* 5 *
************
"""
), "sample 1"
# minimum-size input
assert run(
"""a
"""
) == (
"""***
*a*
***
"""
), "single character"
# all equal lengths
assert run(
"""abc
def
ghi
"""
) == (
"""*****
*abc*
*def*
*ghi*
*****
"""
), "equal lengths"
# alternating odd padding
assert run(
"""abcdef
abc
x
"""
) == (
"""********
*abcdef*
* abc *
* x *
********
"""
), "alternating odd differences"
# empty line handling
assert run(
"""abc
d
"""
) == (
"""*****
*abc*
* *
* d *
*****
"""
), "empty line preserved"
| Test input | Expected output | What it validates |
|---|---|---|
a |
Single centered character | Minimum valid input |
abc def ghi |
No extra spaces added | Equal-length lines |
abcdef abc x |
Alternating odd padding | Tie-breaking correctness |
abc "" d |
Empty line inside frame | Correct handling of blank lines |
Edge Cases
Consider the input:
abc
d
The maximum width is 3. The empty line has length 0, so it needs 3 spaces on each side combined. Since 3 is odd, the algorithm assigns:
left = 1
right = 2
The produced line becomes:
* *
The blank line is preserved because the algorithm reads every line from input, including empty ones.
Now consider alternating odd differences:
abcdef
abc
5
The maximum width is 6.
For "abc":
diff = 3
left = 1
right = 2
For "5":
diff = 5
left = 3
right = 2
The flag flips after the first odd case, so the second odd case reverses the larger side. This exactly matches the required alternating behavior.
Finally, consider already aligned text:
abc
def
ghi
Every line already has length 3, so:
diff = 0
No spaces are inserted. The algorithm simply wraps each line with *, producing the smallest possible valid frame.