3019. Number of Changing Keys
Problem Description
In this problem, we're given a 0-indexed string s
that represents the sequence of keys typed by a user. The string could consist of lowercase and uppercase letters from 'a' to 'z'. Our goal is to determine the number of key changes that the user has made. A key change is defined as typing a character that is different from the immediately preceding character, regardless of whether the characters are in different cases or the same (for example, typing 'a' after 'A' is not considered a change). Essentially, we need to count the instances where one character followed by another is different when both characters are converted to lowercase.
Modifiers like 'shift' or 'caps lock' which are used to change the case of letters are not considered key changes in themselves. This means that just switching from uppercase to lowercase or vice versa does not count as a change. The user has to move to a different letter altogether for it to be considered a change of keys.
Intuition
To arrive at the solution for this problem, we need an efficient way to compare consecutive characters in the string and count only the instances where a letter is followed by a different letter. Here's the intuition behind our approach:
- We iterate over the string
s
to examine each character in sequence. - We compare each character with the next one, ignoring case (we use the lowercase of both characters for comparison).
- If the characters differ, it means the user had to change the key, so we increment a counter.
- We continue this process for the entire string, and the sum of increments in the counter gives us the total number of key changes.
So, the key idea is to compare each character with its successor while treating uppercase and lowercase characters as equal. To compare each character with its direct follower efficiently, we use the pairwise
utility, which allows us to iterate over the string in pairs of consecutive characters. For each pair, we compare their lowercase forms to check for a key change. By summing the instances where the comparison is True
(meaning a change has occurred), we derive the total number of key changes.
Solution Approach
Following the intuition from before, the implementation involves traversing the string and performing a simple comparison for each pair of consecutive characters. Here's how it breaks down:
Data Structures and Patterns
We use the pairwise
function, which is typically used to generate a new iterable by returning pairs of elements from the input iterable. In Python, it would provide us with an iterator over pairs of consecutive items. Here, pairwise(s)
will return (s[0], s[1]), (s[1], s[2]), ... (s[n-2], s[n-1]) where n
is the length of the string.
Algorithms
The solution makes use of the following simple algorithm:
-
Convert Characters to Lowercase and Compare: The
pairwise
function gives us each pair of consecutive characters a and b as tuples. For each tuple, we convert a and b to lowercase and then check if they are not equal to each other usinga.lower() != b.lower()
. -
Count Key Changes: If the lowercase versions of a and b are not equal, it indicates a key change. Using a generator expression inside
sum
, we iterate over all pairs, convert to lowercase, compare, and count these instances directly. -
Summation:
sum
is used to add up all theTrue
values from the generator expression, where eachTrue
represents a different comparison and hence a key change.
The algorithm completes in a single pass over the string, which gives us a time complexity of O(n), where n is the length of the string.
def countKeyChanges(self, s: str) -> int:
return sum(a.lower() != b.lower() for a, b in pairwise(s))
This single line of Python code is all that's needed to implement the complete algorithm, leveraging the power of Python's built-in functions and concise syntax.
Efficiency
Since we're performing one iteration of the string, and each operation within that iteration is O(1), the overall time complexity is O(n), making this approach efficient and scalable for large strings. There are no complex data structures involved, and we only require additional space for the lowercase character comparisons, which is constant space O(1), thus making the space complexity of this algorithm also very efficient.
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 consider a small example to illustrate the solution approach, using the string s = "AabB"
. We want to find out the number of key changes the user has made, recalling that a 'key change' is defined as typing a distinct character than the previous one, case-insensitively.
Following the provided solution approach:
-
We will use the
pairwise
function to create pairs of consecutive characters from our strings
, resulting in the pairs: ('A', 'a'), ('a', 'b'), and ('b', 'B'). Here,pairwise(s)
basically generates an iterator that would give us these pairs when looped over. -
For each pair, we convert each element to its lowercase version and compare them:
- Compare
A
witha
: SinceA
.lower() ==a
.lower(), there's no key change. - Compare
a
withb
: Sincea
.lower() !=b
.lower(), we have a key change. - Compare
b
withB
: Sinceb
.lower() ==B
.lower(), there's no key change.
- Compare
-
We use a generator expression within
sum
to count the key changes. In our case, the pairs would give us[False, True, False]
when comparing their lowercased versions. We are only interested in the instances where the comparison yieldsTrue
. -
Finally, we sum up the
True
values usingsum()
, which represents the total key changes. For our example, we getsum([False, True, False])
, which simplistically is just 0 + 1 + 0, equaling 1.
Therefore, using the given method, the user has made 1 key change while typing the string "AabB".
The corresponding Python code using the provided algorithm would process this example as follows:
from itertools import pairwise
def countKeyChanges(s: str) -> int:
return sum(a.lower() != b.lower() for a, b in pairwise(s))
# Example string
s = "AabB"
# Expected output: 1
print(countKeyChanges(s))
When we execute this code with our example string s = "AabB"
, the output will be 1
, which matches our manual calculation.
Solution Implementation
1from itertools import pairwise # pairwise is from itertools in Python 3.10 and later. For earlier versions, you may need to define it manually.
2
3class Solution:
4 def countKeyChanges(self, keyboard_sequence: str) -> int:
5 """
6 Count the number of key changes in a given keyboard sequence.
7
8 :param keyboard_sequence: A string representing the sequence of key presses.
9 :return: An integer representing the number of key changes.
10 """
11
12 # Initialize a counter for key changes
13 key_changes = 0
14
15 # Use the pairwise function to create pairs of consecutive elements
16 for current_key, next_key in pairwise(keyboard_sequence):
17 # Check if the lowercase versions of the consecutive keys are different
18 if current_key.lower() != next_key.lower():
19 # If so, increment the key changes counter
20 key_changes += 1
21
22 # Return the total number of key changes
23 return key_changes
24
25# Example usage:
26# solution = Solution()
27# result = solution.countKeyChanges("aAAbBBbC")
28# print(result) # Output would be the number of key changes.
29```
30
31Key points in this rework:
32- The method name `countKeyChanges` remains unchanged as per the instructions.
33- The `s` parameter is renamed to `keyboard_sequence` to provide more clarity on what it represents.
34- A comment section has been added before the method to explain the functionality, inputs, and output.
35- Added inline comments to explain each step of the code for better understanding.
36
37Remember that the `pairwise` function was added in Python 3.10. If you're using a version of Python prior to 3.10, you would have to define the `pairwise` function manually like this:
38
39```python
40def pairwise(iterable):
41 "s -> (s0,s1), (s1,s2), (s2, s3), ..."
42 a, b = itertools.tee(iterable)
43 next(b, None)
44 return zip(a, b)
45
1class Solution {
2 // Method to count the number of key changes in the input string
3 public int countKeyChanges(String s) {
4 // Initialize the count of key changes to zero
5 int keyChangeCount = 0;
6
7 // Iterate over the characters in the string starting from the second character
8 for (int i = 1; i < s.length(); ++i) {
9 // Compare current and previous characters in a case-insensitive manner
10 if (Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(i - 1))) {
11 // Increment the number of key changes when the current and previous characters differ
12 keyChangeCount++;
13 }
14 }
15
16 // Return the total count of key changes
17 return keyChangeCount;
18 }
19}
20
1#include <algorithm> // Include algorithm header for transform
2#include <string> // Include string header for string class
3
4class Solution {
5public:
6 // This function counts the number of times the key changes (case-insensitive).
7 int countKeyChanges(std::string s) {
8 // Convert the entire string to lower case to make comparison case-insensitive
9 std::transform(s.begin(), s.end(), s.begin(),
10 [](unsigned char c) { return std::tolower(c); });
11
12 // Initialize a counter for the number of key changes
13 int count = 0;
14
15 // Iterate through the string starting from the second character
16 for (size_t i = 1; i < s.size(); ++i) {
17 // If the current character is different from the previous one,
18 // increment the count of key changes
19 count += (s[i] != s[i - 1]);
20 }
21
22 // Return the total count of key changes
23 return count;
24 }
25};
26
1function countKeyChanges(inputString: string): number {
2 // Convert the input string to lowercase for a case-insensitive comparison
3 inputString = inputString.toLowerCase();
4
5 // Initialize a counter for the number of key changes
6 let changeCount = 0;
7
8 // Iterate over the characters of the string, starting from the second character
9 for (let index = 1; index < inputString.length; ++index) {
10 // Compare the current character with the previous one
11 if (inputString[index] !== inputString[index - 1]) {
12 // If they differ, increment the change count
13 ++changeCount;
14 }
15 }
16
17 // Return the total number of key changes in the string
18 return changeCount;
19}
20
Time and Space Complexity
The time complexity of the provided code is O(n)
, where n
is the length of the string s
. This is because the pairwise
function generates pairs of consecutive characters from the string and the sum
function goes through each pair only once in a single pass. Hence, the number of operations is proportional to the number of characters in the string.
The space complexity of the code is O(1)
. This is due to the fact that the pairwise
function does not create a separate list of pairs but instead generates them on-the-fly, and the summation computation does not require additional space that grows with the input size. Only a constant amount of extra memory is used for the iterator and the count variable within the sum, regardless of the size of the string s
.
Learn more about how to find time and space complexity quickly using problem constraints.
How many times is a tree node visited in a depth first search?
Recommended Readings
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
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!