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
textto display - Screen dimensions: width
wand heighth - An array
fontscontaining available font sizes in ascending order - A
FontInfointerface with two methods:getWidth(fontSize, ch): returns the width of characterchat 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]- Or in terms of our feasible function:
[false, false, ..., false, true, true, ..., true]
We define tooLarge(fontSize) to return true if the font is too large to fit. Using the standard binary search template, we find the first index where tooLarge returns true - this is the first font that doesn't fit. The answer is then the font just before this index.
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 (returns true). The height check is quick (O(1)), and if it fails, we can skip the width calculation entirely.
Learn more about Binary Search patterns.
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 def too_large(font_size: int) -> bool:
28 """Check if font size is too large to fit the text."""
29 if fontInfo.getHeight(font_size) > h:
30 return True
31 total_width = sum(fontInfo.getWidth(font_size, char) for char in text)
32 return total_width > w
33
34 # Binary search to find the first font that is too large
35 left, right = 0, len(fonts) - 1
36 first_true_index = -1
37
38 while left <= right:
39 mid = (left + right) // 2
40 if too_large(fonts[mid]):
41 first_true_index = mid
42 right = mid - 1
43 else:
44 left = mid + 1
45
46 # first_true_index is the first font that doesn't fit
47 # So the answer is the font just before it
48 if first_true_index == -1:
49 # All fonts fit, return the largest
50 return fonts[-1]
51 elif first_true_index == 0:
52 # Even the smallest font doesn't fit
53 return -1
54 else:
55 return fonts[first_true_index - 1]
561/**
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 private String text;
13 private int w, h;
14 private FontInfo fontInfo;
15
16 public int maxFont(String text, int w, int h, int[] fonts, FontInfo fontInfo) {
17 this.text = text;
18 this.w = w;
19 this.h = h;
20 this.fontInfo = fontInfo;
21
22 // Binary search to find the first font that is too large
23 int left = 0;
24 int right = fonts.length - 1;
25 int firstTrueIndex = -1;
26
27 while (left <= right) {
28 int mid = left + (right - left) / 2;
29 if (tooLarge(fonts[mid])) {
30 firstTrueIndex = mid;
31 right = mid - 1;
32 } else {
33 left = mid + 1;
34 }
35 }
36
37 // firstTrueIndex is the first font that doesn't fit
38 // So the answer is the font just before it
39 if (firstTrueIndex == -1) {
40 // All fonts fit, return the largest
41 return fonts[fonts.length - 1];
42 } else if (firstTrueIndex == 0) {
43 // Even the smallest font doesn't fit
44 return -1;
45 } else {
46 return fonts[firstTrueIndex - 1];
47 }
48 }
49
50 private boolean tooLarge(int fontSize) {
51 if (fontInfo.getHeight(fontSize) > h) {
52 return true;
53 }
54 int totalWidth = 0;
55 for (char c : text.toCharArray()) {
56 totalWidth += fontInfo.getWidth(fontSize, c);
57 }
58 return totalWidth > w;
59 }
60}
611/**
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 int maxFont(string text, int w, int h, vector<int>& fonts, FontInfo fontInfo) {
16 // Lambda to check if font size is too large
17 auto tooLarge = [&](int fontSize) -> bool {
18 if (fontInfo.getHeight(fontSize) > h) {
19 return true;
20 }
21 int totalWidth = 0;
22 for (char& ch : text) {
23 totalWidth += fontInfo.getWidth(fontSize, ch);
24 }
25 return totalWidth > w;
26 };
27
28 // Binary search to find the first font that is too large
29 int left = 0;
30 int right = fonts.size() - 1;
31 int firstTrueIndex = -1;
32
33 while (left <= right) {
34 int mid = left + (right - left) / 2;
35 if (tooLarge(fonts[mid])) {
36 firstTrueIndex = mid;
37 right = mid - 1;
38 } else {
39 left = mid + 1;
40 }
41 }
42
43 // firstTrueIndex is the first font that doesn't fit
44 // So the answer is the font just before it
45 if (firstTrueIndex == -1) {
46 // All fonts fit, return the largest
47 return fonts.back();
48 } else if (firstTrueIndex == 0) {
49 // Even the smallest font doesn't fit
50 return -1;
51 } else {
52 return fonts[firstTrueIndex - 1];
53 }
54 }
55};
561/**
2 * FontInfo API interface (provided by the system)
3 * Should not be implemented or speculated about its implementation
4 */
5interface FontInfo {
6 getWidth(fontSize: number, ch: string): number;
7 getHeight(fontSize: number): number;
8}
9
10const maxFont = function(
11 text: string,
12 w: number,
13 h: number,
14 fonts: number[],
15 fontInfo: FontInfo
16): number {
17 const tooLarge = (fontSize: number): boolean => {
18 if (fontInfo.getHeight(fontSize) > h) {
19 return true;
20 }
21 let totalWidth = 0;
22 for (const ch of text) {
23 totalWidth += fontInfo.getWidth(fontSize, ch);
24 }
25 return totalWidth > w;
26 };
27
28 // Binary search to find the first font that is too large
29 let left = 0;
30 let right = fonts.length - 1;
31 let firstTrueIndex = -1;
32
33 while (left <= right) {
34 const mid = Math.floor((left + right) / 2);
35 if (tooLarge(fonts[mid])) {
36 firstTrueIndex = mid;
37 right = mid - 1;
38 } else {
39 left = mid + 1;
40 }
41 }
42
43 // firstTrueIndex is the first font that doesn't fit
44 // So the answer is the font just before it
45 if (firstTrueIndex === -1) {
46 // All fonts fit, return the largest
47 return fonts[fonts.length - 1];
48 } else if (firstTrueIndex === 0) {
49 // Even the smallest font doesn't fit
50 return -1;
51 } else {
52 return fonts[firstTrueIndex - 1];
53 }
54};
55Solution Approach
The solution implements the standard binary search template to find the maximum valid font size. Let's walk through the implementation step by step:
1. Helper Function - tooLarge(fontSize):
def too_large(font_size: int) -> bool:
if fontInfo.getHeight(font_size) > h:
return True
total_width = sum(fontInfo.getWidth(font_size, char) for char in text)
return total_width > w
This function returns true if the font size is too large to fit, creating a monotonic pattern:
- For small fonts:
false, false, false, ...(they fit) - For large fonts:
..., true, true, true(they don't fit)
2. Binary Search Setup:
left, right = 0, len(fonts) - 1
first_true_index = -1
leftandrightare indices into thefontsarrayfirst_true_index = -1tracks the first font that is too large (initialized to -1 meaning "not found yet")
3. Binary Search Loop (Standard Template):
while left <= right: mid = (left + right) // 2 if too_large(fonts[mid]): first_true_index = mid right = mid - 1 else: left = mid + 1
This implements the standard "find first true" template:
- Use
while left <= right(notleft < right) - When
tooLarge(fonts[mid])is true, we found a font that doesn't fit - record it infirst_true_indexand search left for an earlier one (right = mid - 1) - When false, the font fits - search right for the boundary (
left = mid + 1)
4. Derive the Answer:
if first_true_index == -1: return fonts[-1] # All fonts fit elif first_true_index == 0: return -1 # Even smallest font doesn't fit else: return fonts[first_true_index - 1] # Font just before first too-large
The answer is the font just before first_true_index:
- If
first_true_index == -1: All fonts fit, return the largest - If
first_true_index == 0: Even the smallest font is too large, return -1 - Otherwise: Return
fonts[first_true_index - 1]
Time Complexity: O(m * log n) where m = len(text) and n = len(fonts)
Space Complexity: O(1) - only using a few variables
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 → total = 6
- For font size 2: height = 4, widths: 'a'=3, 'b'=3, 'c'=3 → total = 9
- For font size 3: height = 6, widths: 'a'=4, 'b'=4, 'c'=4 → total = 12
- For font size 4: height = 8, widths: 'a'=5, 'b'=5, 'c'=5 → total = 15
- For font size 5: height = 10, widths: 'a'=6, 'b'=6, 'c'=6 → total = 18
tooLarge pattern: [false, false, false, true, true] (fonts 1,2,3 fit; fonts 4,5 don't)
Step-by-step Binary Search:
Initial Setup:
left = 0,right = 4first_true_index = -1
Iteration 1:
mid = (0 + 4) // 2 = 2- Check
tooLarge(fonts[2])=tooLarge(3):- Height: 6 ≤ 8 ✓
- Width: 12 ≤ 12 ✓
- Returns
false(font 3 fits)
- Since
tooLargeis false, search right:left = 2 + 1 = 3
Iteration 2:
left = 3,right = 4mid = (3 + 4) // 2 = 3- Check
tooLarge(fonts[3])=tooLarge(4):- Height: 8 ≤ 8 ✓
- Width: 15 > 12 ✗
- Returns
true(font 4 is too large)
- Since
tooLargeis true:first_true_index = 3,right = 3 - 1 = 2
Iteration 3:
left = 3,right = 2left > right, loop terminates
Derive Answer:
first_true_index = 3(first font that doesn't fit is at index 3)- Answer =
fonts[first_true_index - 1]=fonts[2]= 3
Result: The maximum font size that can display "abc" is 3.
This example demonstrates the standard binary search template:
- We track
first_true_indexto find the boundary - The answer is
fonts[first_true_index - 1]- the last font that fits - We checked only 2 font sizes (3 and 4) out of 5 possible options
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
checkfunction is called once - The
checkfunction iterates through all characters in the text string, makingnAPI calls togetWidth()and one call togetHeight() - Each API call is assumed to be
O(1) - Therefore, each
checkfunction 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
checkfunction 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. Using the Wrong Binary Search Template
Pitfall: Using while left < right with left = mid instead of the standard template.
# Wrong variant - prone to infinite loops while left < right: mid = (left + right + 1) // 2 if canFit(fonts[mid]): left = mid else: right = mid - 1
Solution: Use the standard template with first_true_index:
# Correct - standard template first_true_index = -1 while left <= right: mid = (left + right) // 2 if tooLarge(fonts[mid]): first_true_index = mid right = mid - 1 else: left = mid + 1
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 tooLarge(mid): # mid is an index, not a font size! first_true_index = mid
Solution: Always use fonts[mid] when checking:
# Correct if tooLarge(fonts[mid]): # fonts[mid] is the actual font size first_true_index = mid
3. Forgetting Edge Cases When Deriving the Answer
Pitfall: Not handling all three cases for first_true_index:
# Wrong - missing edge cases return fonts[first_true_index - 1]
Solution: Handle all three cases:
# Correct if first_true_index == -1: return fonts[-1] # All fonts fit elif first_true_index == 0: return -1 # No font fits else: return fonts[first_true_index - 1]
4. Integer Overflow in Width Calculation
Pitfall: For very long texts or large font sizes, the sum of character widths might overflow.
Solution: Break early if width exceeds limit:
def too_large(font_size):
if fontInfo.getHeight(font_size) > h:
return True
total_width = 0
for c in text:
total_width += fontInfo.getWidth(font_size, c)
if total_width > w: # Early termination
return True
return False
5. Inverting the Feasible Function Logic
Pitfall: Defining feasible as "font fits" but forgetting to adjust the answer derivation.
The standard template finds the first true. If you define feasible(x) = font fits, then first_true_index is the first font that fits, and you'd need to find the last true instead.
Solution: Define tooLarge(x) = font doesn't fit. This creates the pattern [false, ..., false, true, ..., true], and first_true_index gives you the first font that doesn't fit. The answer is fonts[first_true_index - 1].
Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.
Which of the following method should we use to solve this problem?
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!