The "Greatest English Letter in Upper and Lower Case" problem requires you to find the greatest English letter (alphabet character) that appears in both its uppercase and lowercase forms in a given string s
.
s
consisting of English letters (both uppercase and lowercase).s
.""
.Constraints:
1 <= s.length <= 1000
s
consists of uppercase and lowercase English letters only.The problem asks us to find the "greatest" (largest in the alphabet) English letter that is present in both uppercase and lowercase forms in the string. At first glance, one might think to check every letter from 'A' to 'Z' and see if both cases exist in the string. However, a brute-force approach would require checking each letter for both cases, which could be inefficient if not handled properly.
A more efficient way is to use sets to quickly check for the existence of uppercase and lowercase letters. By storing all characters in the string in a set, we can check for the presence of both cases for each letter in constant time. To ensure we find the "greatest" letter, we can iterate from 'Z' down to 'A', and return as soon as we find a letter that meets the criteria.
This approach avoids unnecessary repeated scans and leverages fast lookups, making it much more optimal than brute-force.
Let's break down the solution into clear steps:
s
into a set. This allows O(1) lookups to check if a character exists in the string.
""
.
Why use a set? Sets allow us to check for the existence of a character in constant time. This is much faster than searching through the string each time.
Why iterate from 'Z' to 'A'? Since we want the greatest letter, we start from the end of the alphabet and return as soon as we find a match.
Let's consider the input s = "lEeTcOdE"
.
Output: "E"
class Solution:
def greatestLetter(self, s: str) -> str:
letters = set(s)
for c in range(ord('Z'), ord('A') - 1, -1):
upper = chr(c)
lower = chr(c + 32)
if upper in letters and lower in letters:
return upper
return ""
class Solution {
public:
string greatestLetter(string s) {
unordered_set<char> letters(s.begin(), s.end());
for (char c = 'Z'; c >= 'A'; --c) {
if (letters.count(c) && letters.count(c + 32)) {
return string(1, c);
}
}
return "";
}
};
class Solution {
public String greatestLetter(String s) {
HashSet<Character> letters = new HashSet<>();
for (char ch : s.toCharArray()) {
letters.add(ch);
}
for (char c = 'Z'; c >= 'A'; --c) {
if (letters.contains(c) && letters.contains((char)(c + 32))) {
return String.valueOf(c);
}
}
return "";
}
}
var greatestLetter = function(s) {
const letters = new Set(s);
for (let code = 90; code >= 65; code--) { // 'Z' to 'A'
const upper = String.fromCharCode(code);
const lower = String.fromCharCode(code + 32);
if (letters.has(upper) && letters.has(lower)) {
return upper;
}
}
return "";
};
Brute-force approach: For each letter from 'A' to 'Z', scan the string twice (once for uppercase, once for lowercase), resulting in O(26 * n), where n is the length of s
.
Optimized approach:
The solution efficiently finds the greatest English letter present in both upper and lower case by leveraging a set for fast existence checks and iterating from 'Z' to 'A' to ensure the greatest is found first. This approach is both simple and optimal, demonstrating the power of set-based lookups and careful iteration order in algorithm design.