2268. Minimum Number of Keypresses
Problem Description
In this problem, you are given the task of simulating typing on a custom keypad that is mapped to the entire set of 26 lowercase English letters. This keypad has 9 buttons, numbered from 1 to 9. The challenge lies in mapping the 26 letters onto these buttons with the following stipulations:
- All letters must be mapped to a button.
- Each letter is mapped to exactly one button.
- Each button can map to at most 3 letters.
Letters are typed by pressing a button multiple times: once for the first letter, twice for the second letter, and thrice for the third letter associated with that button. You are given a string s
, and your task is to determine the minimum number of keypresses required to type out s
using your keypad configuration.
For example, if the string s
is "abcabc" and your button mapping leads to 'a', 'b', and 'c' all being on the first button, the number of keypresses would be 6: one for each letter since they are the first letter on that button.
The problem emphasizes that the specific mapping and the order of the letters on each button is fixed and cannot be changed throughout the typing process.
Intuition
The key to this problem is frequency analysis and achieving an optimal configuration where frequently used letters require fewer keypresses. Since each button can map at most 3 letters, the idea is to assign the most frequently occurring letters in the string s
to the first slot on each button, the next set of frequent letters to the second slot, and so on. Here's the intuition:
- Count the frequency of each letter in the string
s
. - Sort these frequencies in descending order to know which letters are most commonly used.
- Assign the most frequent letters to the first slot of each button (meaning they'll only require one keypress), the next set to the second slots (two keypresses), and the least frequent to the third slots (three keypresses).
- Calculate the total number of keypresses required based on this configuration.
Through this approach, we minimize the number of presses for frequent letters, which overall leads to a reduced total number of keypresses for typing the entire string. The provided solution uses Python's Counter class to count the frequency of each character and a sorting process to implement this idea.
Solution Approach
The solution approach involves the following steps:
-
The
Counter
class from Python'scollections
module is used to create a frequency map (cnt
) of each character in the input strings
. This map keeps track of how many times each character appears. -
We initialize two variables,
ans
to keep a count of the total keypresses andi
to keep a track of the number of buttons already assigned, andj
to track the number of keypresses required for a given slot. -
We sort the frequency map in reverse order, ensuring that the most frequently occurring characters are considered first.
-
We iterate through the sorted frequencies, incrementally assigning characters to the next available slot on the buttons. For each character, we multiply the character's frequency (
v
) with the current required number of keypresses (j
) and add this toans
. -
Every button can hold up to three letters, so we keep a counter
i
that increments with each character assignment. Wheni % 9
is zero, it means we have filled out a complete set of three slots for all nine buttons, and it is time to move to the next set of slots (which requires an additional keypress). That's when we incrementj
. -
After the loop is done,
ans
holds the minimum number of keypresses needed to type the strings
using the optimal keypad configuration as per our frequency analysis.
The pattern used here is Greedy. By allocating the most frequent characters to the key positions that require fewer keypresses, we ensure that the overall cost (total number of keypresses) is minimized, which is a typical hallmark of greedy algorithms.
Below is the code snippet, illustrating how this is implemented:
class Solution:
def minimumKeypresses(self, s: str) -> int:
cnt = Counter(s) # Step 1: Create frequency map of characters
ans = 0
i, j = 0, 1
for v in sorted(cnt.values(), reverse=True): # Step 2 & 3: Iterate over frequencies
i += 1
ans += j * v # Step 4: Calculate cumulative keypresses
if i % 9 == 0: # Step 5: Move to next slot and increment keypress count if needed
j += 1
return ans # Step 6: Return the total keypresses
This solution ensures that we achieve an optimal assignment of characters to keypresses based on their frequency of occurrence in the input string s
.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's go through a small example to illustrate the solution approach. Assume our input string s
is "hello".
Following the steps outlined:
-
We first use the
Counter
class to create a frequency map of the characters in "hello". This results in the following frequency map:{'h': 1, 'e': 1, 'l': 2, 'o': 1}
. -
We initialize
ans = 0
to count total keypresses,i = 0
to track the button assignment, andj = 1
to count the keypresses required for each letter. -
We then sort the frequency map in descending order of frequency, which gives us
{'l': 2, 'h': 1, 'e': 1, 'o': 1}
. -
Iterating through the sorted frequencies, we start by assigning 'l' to the first button (since 'l' has the highest frequency, 2). The total keypresses
ans
now becomej * 2 = 1 * 2 = 2
. -
We assign the next characters 'h', 'e', and 'o' to the first slots on the next buttons. After each assignment, we increment
i
by 1. Since none makei % 9 == 0
, there is no need to incrementj
. The keypresses for each arej * 1 = 1 * 1 = 1
, so we add 1 for each character, makingans = 2 + 1 + 1 + 1 = 5
. -
At the end,
ans
is 5, which is the minimum number of keypresses needed to type "hello" using an optimal keypad configuration.
The resulting keypad map for the example could be as follows (considering only the assignments we made):
Button 1: l Button 2: h Button 3: e Button 4: o
And the total number of keypresses to type "hello" using this map is 5.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def minimumKeypresses(self, s: str) -> int:
5 # Compute the frequency of each character in the string
6 character_frequency = Counter(s)
7
8 # Initialize the total number of key presses
9 total_key_presses = 0
10
11 # Initialize the keys already allocated and the current multiplier
12 allocated_keys, current_multiplier = 0, 1
13
14 # Loop through the character frequencies in descending order
15 for frequency in sorted(character_frequency.values(), reverse=True):
16 allocated_keys += 1 # Increment keys allocated to count this character
17 total_key_presses += current_multiplier * frequency # Add the key presses for this character
18
19 # Every 9th character requires an additional key press (since you can only fit 9 on one screen)
20 if allocated_keys % 9 == 0:
21 current_multiplier += 1 # Increase the multiplier after filling a screen
22
23 # Return the total number of key presses
24 return total_key_presses
25
1class Solution {
2
3 public int minimumKeypresses(String s) {
4 // Initialize a frequency array to store occurrences of each letter
5 int[] frequency = new int[26];
6
7 // Fill the frequency array with counts of each character in the input string
8 for (char character : s.toCharArray()) {
9 frequency[character - 'a']++;
10 }
11
12 // Sort the frequency array in ascending order
13 Arrays.sort(frequency);
14
15 // Initialize variable to store the total number of keypresses
16 int totalKeypresses = 0;
17
18 // Initialize a variable to determine the number of keypresses per character
19 int keypressesPerChar = 1;
20
21 // Loop through the frequency array from the most frequent to the least frequent character
22 for (int i = 1; i <= 26; i++) {
23 // Add to the total keypress count: keypressesPerChar times the frequency of the character
24 totalKeypresses += keypressesPerChar * frequency[26 - i];
25
26 // Every 9th character will require an additional keypress
27 if (i % 9 == 0) {
28 keypressesPerChar++;
29 }
30 }
31
32 // Return the total minimum number of keypresses needed
33 return totalKeypresses;
34 }
35}
36
1#include <vector>
2#include <string>
3#include <algorithm>
4using namespace std;
5
6class Solution {
7public:
8 int minimumKeypresses(string s) {
9 // Create a frequency vector to count the occurrences of each character.
10 vector<int> frequencyCounter(26, 0);
11
12 // Increment the frequency count for each character in the string.
13 for (char& character : s) {
14 ++frequencyCounter[character - 'a'];
15 }
16
17 // Sort the frequency vector in non-increasing order.
18 sort(frequencyCounter.begin(), frequencyCounter.end(), greater<int>());
19
20 // Initialize the answer to 0, which will hold the minimum keypresses required.
21 int minimumKeyPresses = 0;
22
23 // The number of keystrokes needed to type a character is determined by its position
24 // in the sorted frequency list. The most frequent characters take 1 keystroke, the
25 // next 9 take 2 keystrokes, and so on.
26 int keystrokes = 1; // Start with 1 keystroke for the most frequent characters.
27
28 // Loop through the frequency vector to calculate the total number of keypresses.
29 // The frequency array is sorted in non-increasing order, so we start from the most
30 // frequent characters.
31 for (int i = 0; i < 26; ++i) {
32 // Calculate the keypresses required for current character frequency
33 // and add it to minimumKeyPresses.
34 minimumKeyPresses += keystrokes * frequencyCounter[i];
35
36 // Every 9 characters, the number of keypresses increases by 1, since
37 // we are basing our calculation off a 9-key keyboard layout.
38 if ((i + 1) % 9 == 0) {
39 ++keystrokes;
40 }
41 }
42
43 // Return the final count of minimum keypresses needed.
44 return minimumKeyPresses;
45 }
46};
47
1// Import statements are not needed as we are not using modules or external libraries.
2
3// Function to calculate the minimum number of keypresses required to type a string.
4function minimumKeypresses(s: string): number {
5 // Create an array to count the occurrences of each character.
6 const frequencyCounter: number[] = new Array(26).fill(0);
7
8 // Increment the frequency count for each character in the string.
9 for (const character of s) {
10 frequencyCounter[character.charCodeAt(0) - 'a'.charCodeAt(0)]++;
11 }
12
13 // Sort the frequency array in non-increasing order.
14 frequencyCounter.sort((a, b) => b - a);
15
16 // Initialize the answer to 0, which will hold the minimum keypresses required.
17 let minimumKeyPresses: number = 0;
18
19 // The number of keystrokes needed to type a character is determined by its
20 // position in the sorted frequency array. The most frequent characters
21 // take 1 keystroke, the next 9 take 2 keystrokes, and so on.
22 let keystrokes: number = 1; // Start with 1 keystroke for the most frequent characters.
23
24 // Loop through the frequency array to calculate the total number of keypresses.
25 for (let i: number = 0; i < 26; ++i) {
26 // Calculate the keypresses required for the current character frequency
27 // and add it to the minimumKeyPresses total.
28 minimumKeyPresses += keystrokes * frequencyCounter[i];
29
30 // Every 9 characters, the number of keypresses increases by 1, since
31 // we are basing our calculation on a 9-key keyboard layout.
32 if ((i + 1) % 9 === 0) {
33 keystrokes++;
34 }
35 }
36
37 // Return the final count of minimum keypresses needed.
38 return minimumKeyPresses;
39}
40
41// Example of using the function:
42const inputString: string = "exampleusage";
43const result: number = minimumKeypresses(inputString);
44console.log(result); // Logs the minimum keypresses required to type the inputString.
45
Time and Space Complexity
The given Python code aims to compute the minimum number of keypresses required to type a string s
, where characters in the string are sorted by frequency, and each successive 9 characters require one additional keypress.
Time Complexity:
-
cnt = Counter(s)
: Creating a counter for the strings
has a time complexity ofO(n)
, wheren
is the length of strings
, as we have to count the frequency of each character in the string. -
sorted(cnt.values(), reverse=True)
: Sorting the values of the counter has a time complexity ofO(k log k)
, wherek
is the number of distinct characters in the strings
. In the worst case (all characters are distinct),k
can be at most 26 for lowercase English letters, resulting inO(26 log 26)
, which is effectively constant time, but in general, sorting isO(k log k)
. -
The for loop iterates over the sorted frequencies, which in the worst case is
k
. The operations inside the loop are constant time, so the loop contributesO(k)
to the total time complexity.
The overall time complexity is therefore O(n + k log k + k)
. Since k
is much smaller than n
and has an upper limit, we often consider it a constant, leading to a simplified time complexity of O(n)
.
Space Complexity:
-
cnt = Counter(s)
: The space complexity of storing the counter isO(k)
, wherek
is the number of distinct characters present ins
. As with time complexity,k
has an upper bound of 26 for English letters, so this is effectivelyO(1)
constant space. -
The space required for the sorted list of frequencies is also
O(k)
. As before, due to the constant limit onk
, we consider thisO(1)
. -
The variables
ans
,i
, andj
occupy constant space, contributingO(1)
to the space complexity.
Therefore, the total space complexity of the algorithm is O(k)
, which simplifies to O(1)
due to the constant upper bound on k
.
Overall, the given code has a time complexity of O(n)
and a constant space complexity of O(1)
.
Learn more about how to find time and space complexity quickly using problem constraints.
What's the output of running the following function using input [30, 20, 10, 100, 33, 12]
?
1def fun(arr: List[int]) -> List[int]:
2 import heapq
3 heapq.heapify(arr)
4 res = []
5 for i in range(3):
6 res.append(heapq.heappop(arr))
7 return res
8
1public static int[] fun(int[] arr) {
2 int[] res = new int[3];
3 PriorityQueue<Integer> heap = new PriorityQueue<>();
4 for (int i = 0; i < arr.length; i++) {
5 heap.add(arr[i]);
6 }
7 for (int i = 0; i < 3; i++) {
8 res[i] = heap.poll();
9 }
10 return res;
11}
12
1class HeapItem {
2 constructor(item, priority = item) {
3 this.item = item;
4 this.priority = priority;
5 }
6}
7
8class MinHeap {
9 constructor() {
10 this.heap = [];
11 }
12
13 push(node) {
14 // insert the new node at the end of the heap array
15 this.heap.push(node);
16 // find the correct position for the new node
17 this.bubble_up();
18 }
19
20 bubble_up() {
21 let index = this.heap.length - 1;
22
23 while (index > 0) {
24 const element = this.heap[index];
25 const parentIndex = Math.floor((index - 1) / 2);
26 const parent = this.heap[parentIndex];
27
28 if (parent.priority <= element.priority) break;
29 // if the parent is bigger than the child then swap the parent and child
30 this.heap[index] = parent;
31 this.heap[parentIndex] = element;
32 index = parentIndex;
33 }
34 }
35
36 pop() {
37 const min = this.heap[0];
38 this.heap[0] = this.heap[this.size() - 1];
39 this.heap.pop();
40 this.bubble_down();
41 return min;
42 }
43
44 bubble_down() {
45 let index = 0;
46 let min = index;
47 const n = this.heap.length;
48
49 while (index < n) {
50 const left = 2 * index + 1;
51 const right = left + 1;
52
53 if (left < n && this.heap[left].priority < this.heap[min].priority) {
54 min = left;
55 }
56 if (right < n && this.heap[right].priority < this.heap[min].priority) {
57 min = right;
58 }
59 if (min === index) break;
60 [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61 index = min;
62 }
63 }
64
65 peek() {
66 return this.heap[0];
67 }
68
69 size() {
70 return this.heap.length;
71 }
72}
73
74function fun(arr) {
75 const heap = new MinHeap();
76 for (const x of arr) {
77 heap.push(new HeapItem(x));
78 }
79 const res = [];
80 for (let i = 0; i < 3; i++) {
81 res.push(heap.pop().item);
82 }
83 return res;
84}
85
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
LeetCode 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 we
Want a Structured Path to Master System Design Too? Don’t Miss This!