A clear explanation of fitting a sentence onto a screen using cyclic string simulation and greedy row transitions.
Problem Restatement
We are given:
| Value | Meaning |
|---|---|
sentence | A list of words |
rows | Number of screen rows |
cols | Number of columns per row |
We want to place the sentence repeatedly on the screen.
Rules:
- Words must appear in the original order.
- A word cannot be split across lines.
- Consecutive words are separated by one space.
- The sentence repeats cyclically.
Return how many complete times the sentence fits on the screen.
The source examples include:
sentence = ["hello", "world"]
rows = 2
cols = 8whose answer is 1.
The constraints include:
1 <= sentence.length <= 100
1 <= rows, cols <= 20000
1 <= word.length <= 10Input and Output
| Item | Meaning |
|---|---|
| Input | sentence, rows, cols |
| Output | Number of complete sentence repetitions |
| Word rule | Words cannot split across rows |
| Space rule | One space between adjacent words |
| Order rule | Sentence order must remain unchanged |
Example function shape:
def wordsTyping(sentence: list[str], rows: int, cols: int) -> int:
...Examples
Example 1:
sentence = ["hello", "world"]
rows = 2
cols = 8The answer is:
1Screen layout:
hello---
world---The sentence fits exactly once.
Example 2:
sentence = ["a", "bcd", "e"]
rows = 3
cols = 6The answer is:
2One valid layout:
a-bcd-
e-a---
bcd-e-The sentence repeats twice completely.
Example 3:
sentence = ["i", "had", "apple", "pie"]
rows = 4
cols = 5The answer is:
1First Thought: Simulate Word by Word
A direct approach is:
- Track the current word index.
- Fill each row greedily.
- Count how many times we return to word
0.
This works, but repeatedly checking words and spaces row by row can become verbose.
We can simplify the process using a cyclic sentence string.
Key Insight
Join the sentence into one cyclic string:
s = "hello world "Notice the trailing space.
Now the sentence behaves like an infinite repeating string:
hello world hello world hello world ...For each row:
- Pretend we advance exactly
colscharacters. - If we land on a space, we can start the next row immediately after that space.
- Otherwise, we landed in the middle of a word, so move backward until we reach a space.
This simulates maximal fitting per row.
At the end, the total number of complete sentence repetitions is:
position // len(s)because every full cycle through s represents one complete sentence.
Algorithm
- Join the sentence into:
s = " ".join(sentence) + " "- Let:
position = 0- For each row:
- Add
colstoposition. - If
s[position % len(s)]is a space:- Move forward one more character.
- Otherwise:
- Move backward until the previous character is a space.
- Add
- Return:
position // len(s)Correctness
The string:
s = " ".join(sentence) + " "contains exactly one complete sentence followed by a trailing separator space.
The variable position tracks how many characters of the infinite repeated sentence stream have been consumed by the screen.
For each row, adding cols greedily attempts to place as many characters as possible.
If the landing position is a space, then the row ended exactly at a word boundary, so the next row can start immediately after that space.
If the landing position is inside a word, words are not allowed to split across rows. The algorithm moves backward until it reaches the nearest valid word boundary.
Thus each row contains the maximum valid prefix that fits within cols characters.
After all rows are processed, every complete traversal of s corresponds to one complete sentence placement.
Therefore:
position // len(s)is exactly the number of complete sentence repetitions that fit on the screen.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(rows + total_backtracking) | Each row advances once |
| Space | O(L) | We store the joined sentence string |
Here:
| Symbol | Meaning |
|---|---|
L | Length of the joined sentence string |
In practice, the backward adjustment is amortized efficient because the pointer never repeatedly scans long distances overall.
Implementation
from typing import List
class Solution:
def wordsTyping(self, sentence: List[str], rows: int, cols: int) -> int:
s = " ".join(sentence) + " "
n = len(s)
position = 0
for _ in range(rows):
position += cols
if s[position % n] == " ":
position += 1
else:
while position > 0 and s[(position - 1) % n] != " ":
position -= 1
return position // nCode Explanation
We first build one cyclic sentence string:
s = " ".join(sentence) + " "For:
["hello", "world"]this becomes:
"hello world "The trailing space is important because it cleanly separates sentence repetitions.
We store the length:
n = len(s)The variable:
positiontracks how many characters have been consumed from the infinite repeated sentence.
For each row:
position += colsgreedily fills the row.
If we land exactly on a space:
if s[position % n] == " ":then the next row can begin immediately after that space:
position += 1Otherwise, we landed in the middle of a word.
Since words cannot split across rows, we move backward:
while position > 0 and s[(position - 1) % n] != " ":
position -= 1until we reach the previous space.
Finally:
return position // ncounts how many complete sentence cycles were consumed.
Alternative DP Optimization
When rows is very large, we can precompute row transitions.
For each possible starting word index:
| Value | Meaning |
|---|---|
| Next starting word index after one row | |
| Number of completed sentence cycles in that row |
Then each row becomes an O(1) lookup.
This optimization reduces repeated scanning when many rows begin with the same word.
Testing
def test_words_typing():
s = Solution()
assert s.wordsTyping(["hello", "world"], 2, 8) == 1
assert s.wordsTyping(["a", "bcd", "e"], 3, 6) == 2
assert s.wordsTyping(["i", "had", "apple", "pie"], 4, 5) == 1
assert s.wordsTyping(["a"], 3, 1) == 3
assert s.wordsTyping(["abc"], 2, 2) == 0
assert s.wordsTyping(["a", "b"], 1, 3) == 1
assert s.wordsTyping(["longword"], 2, 8) == 2
print("all tests passed")Test Notes
| Test | Why |
|---|---|
| Standard example 1 | Exact row fitting |
| Standard example 2 | Multiple sentence repetitions |
| Standard example 3 | Mixed word lengths |
| Single short word | Repeated many times |
| Word longer than row | Cannot fit |
| Exact fit with space | Boundary handling |
| Word length equals columns | Full-row single word |