1618. Maximum Font to Fit a Sentence in a Screen 🔒
Problem Description
You need to find the maximum font size that can be used to display a given string text
on a screen with width w
and height h
.
You're given:
- A string
text
to display - Screen dimensions: width
w
and heighth
- An array
fonts
containing available font sizes in ascending order - A
FontInfo
interface with two methods:getWidth(fontSize, ch)
: returns the width of characterch
at the given font sizegetHeight(fontSize)
: returns the height of any character at the given font size
The text will be displayed on a single line. For the text to fit on the screen with a particular font size:
- The total width (sum of widths of all characters) must be ≤
w
- The height must be ≤
h
The problem guarantees that:
- Font metrics are consistent (same parameters always return same values)
- Larger font sizes have larger or equal dimensions:
getHeight(fontSize) ≤ getHeight(fontSize+1)
getWidth(fontSize, ch) ≤ getWidth(fontSize+1, ch)
The solution uses binary search on the fonts
array. The check
function verifies if a given font size works by:
- Checking if the height fits:
fontInfo.getHeight(size) ≤ h
- Checking if the total width fits: sum of all character widths ≤
w
Since fonts
is sorted and larger fonts have larger dimensions, binary search efficiently finds the maximum valid font size. If no font size works, return -1
.
Intuition
The key insight is recognizing this as an optimization problem with a monotonic property. Since the fonts
array is sorted in ascending order and larger font sizes produce larger dimensions (both width and height), we have a clear pattern: if a font size is too large to fit, all larger font sizes will also be too large.
This monotonic property immediately suggests binary search. Think of it this way - we're looking for a threshold: the largest font size that still fits. Everything below this threshold works, everything above doesn't work. This creates a partition in our sorted fonts
array:
[fonts that fit] | [fonts that don't fit]
We want to find the last element in the "fonts that fit" section.
Why not just check each font size from largest to smallest? While this would work, it could require checking every font size in the worst case, giving us O(n)
checks where n
is the number of fonts. Binary search reduces this to O(log n)
checks.
The checking logic itself is straightforward: for any given font size, we need to verify two constraints:
- Height constraint: The font height must fit within screen height
h
- Width constraint: The sum of all character widths must fit within screen width
w
If either constraint fails, the font is too large. The height check is quick (O(1)
), and if it fails, we can skip the width calculation entirely. The width check requires summing widths for all characters (O(m)
where m
is text length), but we only do this O(log n)
times due to binary search.
The binary search implementation uses the pattern of finding the rightmost valid position - we're looking for the maximum valid font size, not just any valid font size. This is why when we find a valid font at mid
, we search the right half (left = mid
) to potentially find an even larger valid font.
Learn more about Binary Search patterns.
Solution Approach
The solution implements a binary search algorithm to find the maximum valid font size. Let's walk through the implementation step by step:
1. Helper Function - check(size)
:
def check(size):
if fontInfo.getHeight(size) > h:
return False
return sum(fontInfo.getWidth(size, c) for c in text) <= w
This function validates whether a given font size can display the text within screen constraints:
- First, it checks the height constraint. If
fontInfo.getHeight(size) > h
, the font is too tall and we returnFalse
immediately - If height is valid, it calculates the total width by summing
fontInfo.getWidth(size, c)
for each characterc
in the text - Returns
True
if total width ≤w
, otherwiseFalse
2. Binary Search Setup:
left, right = 0, len(fonts) - 1
ans = -1
left
andright
are indices into thefonts
array, not the font sizes themselves- We search the index range
[0, len(fonts)-1]
ans
is initialized to-1
(the default return value if no font works)
3. Binary Search Loop:
while left < right: mid = (left + right + 1) >> 1 if check(fonts[mid]): left = mid else: right = mid - 1
This implements the "find rightmost valid position" pattern:
- Calculate
mid = (left + right + 1) >> 1
. The+1
is crucial here - it ensures we round up whenleft
andright
are adjacent, preventing infinite loops - The
>> 1
is a bitwise right shift, equivalent to integer division by 2 - If
fonts[mid]
is valid (text fits), we search the right half by settingleft = mid
, looking for potentially larger valid fonts - If
fonts[mid]
is invalid (text doesn't fit), we search the left half by settingright = mid - 1
4. Final Check and Return:
return fonts[left] if check(fonts[left]) else -1
After the loop, left
equals right
, pointing to our candidate answer. We perform one final validation:
- If
check(fonts[left])
isTrue
, returnfonts[left]
(the actual font size, not the index) - Otherwise, return
-1
indicating no valid font size exists
Time Complexity:
- Binary search performs
O(log n)
iterations wheren = len(fonts)
- Each
check
call takesO(m)
time wherem = len(text)
due to the width summation - Total:
O(m * log n)
Space Complexity:
O(1)
- only using a few variables for the binary search
The algorithm efficiently finds the maximum usable font size by leveraging the monotonic property of font sizes and binary search's logarithmic time complexity.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Given:
text = "abc"
w = 12
(screen width)h = 8
(screen height)fonts = [1, 2, 3, 4, 5]
- FontInfo returns:
- For font size 1: height = 2, widths: 'a'=2, 'b'=2, 'c'=2
- For font size 2: height = 4, widths: 'a'=3, 'b'=3, 'c'=3
- For font size 3: height = 6, widths: 'a'=4, 'b'=4, 'c'=4
- For font size 4: height = 8, widths: 'a'=5, 'b'=5, 'c'=5
- For font size 5: height = 10, widths: 'a'=6, 'b'=6, 'c'=6
Step-by-step Binary Search:
Initial Setup:
left = 0
,right = 4
(indices into fonts array)ans = -1
Iteration 1:
mid = (0 + 4 + 1) >> 1 = 2
- Check
fonts[2] = 3
:- Height check:
getHeight(3) = 6 ≤ 8
✓ - Width check:
getWidth(3,'a') + getWidth(3,'b') + getWidth(3,'c') = 4 + 4 + 4 = 12 ≤ 12
✓ - Font size 3 works!
- Height check:
- Since it works, search right half:
left = 2
Iteration 2:
- Now
left = 2
,right = 4
mid = (2 + 4 + 1) >> 1 = 3
- Check
fonts[3] = 4
:- Height check:
getHeight(4) = 8 ≤ 8
✓ - Width check:
5 + 5 + 5 = 15 ≤ 12
✗ - Font size 4 doesn't work!
- Height check:
- Since it doesn't work, search left half:
right = 3 - 1 = 2
Iteration 3:
- Now
left = 2
,right = 2
- Loop terminates (left == right)
Final Check:
left = 2
, so we checkfonts[2] = 3
- We already know from iteration 1 that font size 3 works
- Return
fonts[2] = 3
Result: The maximum font size that can display "abc" is 3.
This example demonstrates how binary search efficiently narrows down the search space:
- We only checked 2 font sizes (3 and 4) out of 5 possible options
- We found that font size 3 fits perfectly (uses exactly the width limit)
- Font size 4 would exceed the width, even though the height would fit
- The algorithm correctly identified the boundary between valid and invalid font sizes
Solution Implementation
1# """
2# This is FontInfo's API interface.
3# You should not implement it, or speculate about its implementation
4# """
5# class FontInfo(object):
6# Return the width of char ch when fontSize is used.
7# def getWidth(self, fontSize, ch):
8# """
9# :type fontSize: int
10# :type ch: char
11# :rtype int
12# """
13#
14# def getHeight(self, fontSize):
15# """
16# :type fontSize: int
17# :rtype int
18# """
19
20from typing import List
21
22
23class Solution:
24 def maxFont(
25 self, text: str, w: int, h: int, fonts: List[int], fontInfo: 'FontInfo'
26 ) -> int:
27 """
28 Find the maximum font size that can fit the text within given width and height constraints.
29
30 Args:
31 text: The text string to display
32 w: Maximum width available
33 h: Maximum height available
34 fonts: List of available font sizes in ascending order
35 fontInfo: FontInfo object to get width and height information
36
37 Returns:
38 Maximum font size that fits, or -1 if no font size fits
39 """
40
41 def can_fit_text(font_size: int) -> bool:
42 """
43 Check if text can fit within constraints using given font size.
44
45 Args:
46 font_size: The font size to check
47
48 Returns:
49 True if text fits within width and height constraints, False otherwise
50 """
51 # Check if height constraint is satisfied
52 if fontInfo.getHeight(font_size) > h:
53 return False
54
55 # Calculate total width of text and check if it fits within width constraint
56 total_width = sum(fontInfo.getWidth(font_size, char) for char in text)
57 return total_width <= w
58
59 # Binary search to find the maximum valid font size
60 left_idx = 0
61 right_idx = len(fonts) - 1
62
63 # Binary search for the rightmost valid font size
64 while left_idx < right_idx:
65 # Calculate middle index (biased towards right for finding maximum)
66 mid_idx = (left_idx + right_idx + 1) >> 1 # Equivalent to // 2
67
68 if can_fit_text(fonts[mid_idx]):
69 # If current font size fits, search in right half for larger sizes
70 left_idx = mid_idx
71 else:
72 # If current font size doesn't fit, search in left half for smaller sizes
73 right_idx = mid_idx - 1
74
75 # Check if the final font size is valid
76 if can_fit_text(fonts[left_idx]):
77 return fonts[left_idx]
78 else:
79 return -1
80
1/**
2 * // This is the FontInfo's API interface.
3 * // You should not implement it, or speculate about its implementation
4 * interface FontInfo {
5 * // Return the width of char ch when fontSize is used.
6 * public int getWidth(int fontSize, char ch) {}
7 * // Return Height of any char when fontSize is used.
8 * public int getHeight(int fontSize)
9 * }
10 */
11class Solution {
12 /**
13 * Finds the maximum font size that allows the text to fit within the given dimensions.
14 * Uses binary search on the sorted fonts array to find the largest valid font size.
15 *
16 * @param text The string to be displayed
17 * @param w Maximum allowed width
18 * @param h Maximum allowed height
19 * @param fonts Array of available font sizes in ascending order
20 * @param fontInfo API to get font dimensions
21 * @return The maximum font size that fits, or -1 if none fit
22 */
23 public int maxFont(String text, int w, int h, int[] fonts, FontInfo fontInfo) {
24 // Initialize binary search boundaries
25 int leftIndex = 0;
26 int rightIndex = fonts.length - 1;
27
28 // Binary search for the maximum valid font size
29 while (leftIndex < rightIndex) {
30 // Calculate mid point, biased towards right for finding maximum
31 int midIndex = (leftIndex + rightIndex + 1) >> 1;
32
33 // Check if text fits with the font at midIndex
34 if (canTextFit(text, fonts[midIndex], w, h, fontInfo)) {
35 // Font fits, try to find a larger one
36 leftIndex = midIndex;
37 } else {
38 // Font doesn't fit, search for smaller fonts
39 rightIndex = midIndex - 1;
40 }
41 }
42
43 // Verify the final font size and return result
44 return canTextFit(text, fonts[leftIndex], w, h, fontInfo) ? fonts[leftIndex] : -1;
45 }
46
47 /**
48 * Checks if the given text can fit within the specified dimensions using the given font size.
49 *
50 * @param text The string to check
51 * @param fontSize The font size to test
52 * @param maxWidth Maximum allowed width
53 * @param maxHeight Maximum allowed height
54 * @param fontInfo API to get font dimensions
55 * @return true if the text fits, false otherwise
56 */
57 private boolean canTextFit(String text, int fontSize, int maxWidth, int maxHeight, FontInfo fontInfo) {
58 // Check if font height exceeds the maximum allowed height
59 if (fontInfo.getHeight(fontSize) > maxHeight) {
60 return false;
61 }
62
63 // Calculate total width required for the text
64 int totalWidth = 0;
65 for (char character : text.toCharArray()) {
66 totalWidth += fontInfo.getWidth(fontSize, character);
67 }
68
69 // Check if total width is within the maximum allowed width
70 return totalWidth <= maxWidth;
71 }
72}
73
1/**
2 * // This is the FontInfo's API interface.
3 * // You should not implement it, or speculate about its implementation
4 * class FontInfo {
5 * public:
6 * // Return the width of char ch when fontSize is used.
7 * int getWidth(int fontSize, char ch);
8 *
9 * // Return Height of any char when fontSize is used.
10 * int getHeight(int fontSize)
11 * };
12 */
13class Solution {
14public:
15 /**
16 * Find the maximum font size that can fit the text within given width and height constraints.
17 *
18 * @param text The string to be displayed
19 * @param w Maximum width available
20 * @param h Maximum height available
21 * @param fonts Vector of available font sizes in ascending order
22 * @param fontInfo Object to query font dimensions
23 * @return Maximum font size that fits, or -1 if none fits
24 */
25 int maxFont(string text, int w, int h, vector<int>& fonts, FontInfo fontInfo) {
26 // Lambda function to check if a given font size fits within constraints
27 auto canFitText = [&](int fontSize) -> bool {
28 // Check if height constraint is satisfied
29 if (fontInfo.getHeight(fontSize) > h) {
30 return false;
31 }
32
33 // Calculate total width required for the text at given font size
34 int totalWidth = 0;
35 for (char& ch : text) {
36 totalWidth += fontInfo.getWidth(fontSize, ch);
37 }
38
39 // Check if width constraint is satisfied
40 return totalWidth <= w;
41 };
42
43 // Binary search to find the maximum valid font size
44 int left = 0;
45 int right = fonts.size() - 1;
46
47 while (left < right) {
48 // Calculate mid point, biasing towards the right for upper bound search
49 int mid = (left + right + 1) >> 1;
50
51 if (canFitText(fonts[mid])) {
52 // If current font size fits, try to find a larger one
53 left = mid;
54 } else {
55 // If current font size doesn't fit, search for smaller ones
56 right = mid - 1;
57 }
58 }
59
60 // Verify the final font size and return result
61 return canFitText(fonts[left]) ? fonts[left] : -1;
62 }
63};
64
1/**
2 * FontInfo API interface (provided by the system)
3 * Should not be implemented or speculated about its implementation
4 */
5interface FontInfo {
6 /**
7 * Get the width of a character at a specific font size
8 * @param fontSize - The font size to use
9 * @param ch - The character to measure
10 * @returns The width of the character in pixels
11 */
12 getWidth(fontSize: number, ch: string): number;
13
14 /**
15 * Get the height of text at a specific font size
16 * @param fontSize - The font size to use
17 * @returns The height of the text in pixels
18 */
19 getHeight(fontSize: number): number;
20}
21
22/**
23 * Find the maximum font size that allows the text to fit within given dimensions
24 * @param text - The text string to display
25 * @param w - The maximum width available in pixels
26 * @param h - The maximum height available in pixels
27 * @param fonts - Array of available font sizes in ascending order
28 * @param fontInfo - FontInfo object to query character dimensions
29 * @returns The maximum font size that fits, or -1 if no font size fits
30 */
31const maxFont = function(
32 text: string,
33 w: number,
34 h: number,
35 fonts: number[],
36 fontInfo: FontInfo
37): number {
38 /**
39 * Check if the text fits within the given dimensions at a specific font size
40 * @param fontSize - The font size to check
41 * @returns True if the text fits, false otherwise
42 */
43 const canTextFitAtSize = function(fontSize: number): boolean {
44 // First check if height constraint is satisfied
45 if (fontInfo.getHeight(fontSize) > h) {
46 return false;
47 }
48
49 // Calculate total width of the text at this font size
50 let totalWidth = 0;
51 for (const character of text) {
52 totalWidth += fontInfo.getWidth(fontSize, character);
53 }
54
55 // Check if width constraint is satisfied
56 return totalWidth <= w;
57 };
58
59 // Binary search for the maximum valid font size
60 let leftIndex = 0;
61 let rightIndex = fonts.length - 1;
62
63 while (leftIndex < rightIndex) {
64 // Calculate mid point, biased towards right for finding maximum
65 const midIndex = (leftIndex + rightIndex + 1) >> 1;
66
67 if (canTextFitAtSize(fonts[midIndex])) {
68 // Font size fits, try to find a larger one
69 leftIndex = midIndex;
70 } else {
71 // Font size doesn't fit, search for smaller ones
72 rightIndex = midIndex - 1;
73 }
74 }
75
76 // Verify the final font size and return result
77 return canTextFitAtSize(fonts[leftIndex]) ? fonts[leftIndex] : -1;
78};
79
Time and Space Complexity
Time Complexity: O(n * log(m))
where n
is the length of the text string and m
is the number of fonts in the fonts array.
- The binary search performs
O(log(m))
iterations to find the maximum valid font size - In each iteration of the binary search, the
check
function is called once - The
check
function iterates through all characters in the text string, makingn
API calls togetWidth()
and one call togetHeight()
- Each API call is assumed to be
O(1)
- Therefore, each
check
function call takesO(n)
time - Total time complexity:
O(n * log(m))
Space Complexity: O(1)
- The algorithm uses only a constant amount of extra space for variables (
left
,right
,ans
,mid
) - The
check
function uses a generator expression withsum()
which processes elements one at a time without storing them - No additional data structures are created that scale with input size
- The space used does not depend on the length of the text or the number of fonts
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Binary Search Midpoint Calculation
Pitfall: Using mid = (left + right) // 2
instead of mid = (left + right + 1) // 2
when searching for the maximum valid value.
When left
and right
are adjacent (e.g., left = 3, right = 4
), using (left + right) // 2
gives mid = 3
. If fonts[3]
is valid, we set left = mid = 3
, causing an infinite loop since the search space doesn't shrink.
Solution: Always use mid = (left + right + 1) // 2
when searching for the rightmost valid position. This ensures mid
rounds up when left
and right
are adjacent, preventing infinite loops.
2. Confusing Array Indices with Font Sizes
Pitfall: Mixing up the index in the fonts
array with the actual font size value.
# Wrong - treating index as font size if check(mid): # mid is an index, not a font size! left = mid
Solution: Always use fonts[mid]
when calling check()
and remember that left
, right
, and mid
are indices:
# Correct if check(fonts[mid]): # fonts[mid] is the actual font size left = mid
3. Forgetting the Final Validation
Pitfall: Directly returning fonts[left]
without checking if it's actually valid.
# Wrong - assumes fonts[left] is always valid return fonts[left]
This is problematic when no font size works (e.g., even the smallest font is too large).
Solution: Always validate the final result before returning:
# Correct return fonts[left] if check(fonts[left]) else -1
4. Integer Overflow in Width Calculation
Pitfall: For very long texts or large font sizes, the sum of character widths might overflow:
# Potential overflow for large values
total_width = sum(fontInfo.getWidth(size, c) for c in text)
Solution: Check for overflow or break early if width exceeds limit:
def check(size):
if fontInfo.getHeight(size) > h:
return False
total_width = 0
for c in text:
total_width += fontInfo.getWidth(size, c)
if total_width > w: # Early termination
return False
return True
5. Wrong Binary Search Pattern
Pitfall: Using the wrong binary search pattern (finding minimum vs maximum):
# Wrong pattern for finding maximum while left < right: mid = (left + right) // 2 # Wrong midpoint for maximum if check(fonts[mid]): left = mid + 1 # Wrong: skips valid values else: right = mid
Solution: Use the correct pattern for finding maximum valid value:
# Correct pattern for finding maximum while left < right: mid = (left + right + 1) // 2 # Correct midpoint if check(fonts[mid]): left = mid # Keep valid value in range else: right = mid - 1 # Exclude invalid value
How does merge sort divide the problem into subproblems?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!