Skip to content

https://leetcode.com/problems/find-smallest-letter-greater-than-target/description/

You are given an array of characters letters that is sorted in non-decreasing order, and a character target. There are at least two different characters in letters.

Return the smallest character in letters that is lexicographically greater than target. If such a character does not exist, return the first character in letters.

Example 1:

Input: letters = ["c","f","j"], target = "a"
Output: "c"
Explanation: The smallest character that is lexicographically greater than 'a' in letters is 'c'.

Example 2:

Input: letters = ["c","f","j"], target = "c"
Output: "f"
Explanation: The smallest character that is lexicographically greater than 'c' in letters is 'f'.

Example 3:

Input: letters = ["x","x","y","y"], target = "z"
Output: "x"
Explanation: There are no characters in letters that is lexicographically greater than 'z' so we return letters[0].

Constraints:

  • 2 <= letters.length <= 104
  • letters[i] is a lowercase English letter.
  • letters is sorted in non-decreasing order.
  • letters contains at least two different characters.
  • target is a lowercase English letter.
c++
class Solution {
public:
    char nextGreatestLetter(vector<char>& letters, char target) {
      	//idea
        //use binary search to find the char that greater than target.
        //if target is less than [i] then check right part.
        //if target equals [i] then check if [i+1] greater than target, then return [i+1]. but check i+1 overflow first.
        //if target greater than [i] then check if [i-1] equals target
        int N=letters.size();
        int low=0, high=N-1;
        for(int mid=high>>1;low<=high;){
            if(target==letters[mid]){
                if(mid+1<N&&target<letters[mid+1]) {
                    return letters[mid+1];
                }
                // check right part
                low=mid+1;
                mid=low+((high-low)>>1);
            } else if(target<letters[mid]) {
                //ensure [mid] is the first to be greater than target
                if(mid-1>=0&&target>=letters[mid-1]){
                    return letters[mid];
                }
                if(mid==0) {
                    return letters[0];
                }
                //check left
                high=mid-1;
                mid=low+((high-low)>>1);
            } else { // target>letters[mid]
                //check right
                low=mid+1;
                mid=low+((high-low)>>1);
            }
        }
        return letters[0];
    }
};

Of course you can use upper_bound.

c++
auto it = upper_bound(letters.begin(), letters.end(), target);
if(it == letters.end()) {
  return letters[0];
}else{
  return *it;
}

Or even just one line

c++
return letters[(upper_bound(letters.begin(), letters.end(), target)-letters.begin())%letters.size()];

[2023-09-26 Tue 14:50]

This solution is inspired by find max square root of x. We store an intermediate variable.

Notice that the candidate answer is always in "greater branch", thus in that branch, we store the candidate mid.

c++
class Solution {
public:
    char nextGreatestLetter(vector<char>& letters, char target) {
        int N=letters.size();
        int ans=0;
        int low=0, high=N-1;
        while(low<=high) {
            int mid=(low+high)>>1;
            if (letters[mid] == target 
                && mid+1 < N && letters[mid+1] > target) {
                return letters[mid+1];
            }
            
            if (letters[mid]<=target) {
                low=mid+1;
            } else {
                // greater branch
                high=mid-1;
                ans=mid;
            }
        }
        //not found
        if(letters[ans]<=target) {
            ans=0;
        }

        return letters[ans];
    }
};