Talentd
FleetCode
Make and follow sheets.Sign in

Last Substring in Lexicographical Order solution and explanation

HardStringTwo Pointers6 min read35.1% acceptanceUpdated 9 Apr 2026
LeetCode 1163. Last Substring in Lexicographical OrderHappy Coding · 23:56 · 5K viewsWatch on YouTube
9 more videos on this problem
  1. LeetCode 1163. Last Substring in Lexicographical OrderHappy Coding · 23:56 · 5K views
  2. LeetCode 1163. Last Substring in Lexicographical OrdercoderZ · 8:02 · 3K views
  3. Last Substring in Lexicographical Order || LeetCodeSiddharth Nagaych · 7:10 · 952 views
  4. 1163. Last Substring in Lexicographical Order - LeetCode - [ Must do for Problem 3403 ]Intuit & Code · 35:50 · 733 views
  5. Finding the last substring in lexicographical order in O(n) time (2 Solutions!!)Roel Van de Paar · 1:53 · 531 views
  6. Leetcode 1163. Last Substring in Lexicographical Order (two pointers)LetsCode · 11:12 · 447 views
  7. 1163. Last Substring in Lexicographical OrderJust Coding, with Jie · 28:23 · 175 views
  8. No Sound Leetcode 1163: Last Substring in Lexicographical OrderExtremeProgrammer · 0:52 · 59 views
  9. LeetCode 1163: Last Substring in Lexicographical OrderAlitaCode · 28:08 · 26 views
  10. LeetCode Problem #1163 Last Substring in Lexicographical order神戸万事屋 · 0:52

The problem

Given a string s, return the last substring of s in lexicographical order.

Example 1:

Input: s = "abab"
Output: "bab"
Explanation: The substrings are ["a", "ab", "aba", "abab", "b", "ba", "bab"]. The lexicographically maximum substring is "bab".

Example 2:

Input: s = "leetcode"
Output: "tcode"

Constraints:

  • 1 <= s.length <= 4 * 105
  • s contains only lowercase English letters.

Approach overview

Problem Overview: Given a string s, return the substring that is lexicographically largest among all possible substrings. Every suffix of the string is a candidate, so the task reduces to finding the maximum suffix in dictionary order.

Approach 1: Suffix Comparison Using Two Pointers (O(n) time, O(1) space)

This optimal approach treats the problem as finding the best starting index of a suffix. Maintain two candidate indices i and j with an offset k. Compare characters s[i + k] and s[j + k]. If they match, increase k. If one suffix is smaller, discard it by moving the pointer forward past the compared range. The key insight: once a suffix loses a comparison, every prefix of that suffix also loses, so you can skip a whole block of candidates instead of checking each one. This creates a linear scan over the string without revisiting characters. The final index points to the lexicographically largest suffix, which is the required substring. This technique resembles competitive suffix comparison strategies and works well for problems involving two pointers and heavy string comparisons.

Approach 2: Suffix Array with LCP (O(n log n) time, O(n) space)

Another method constructs a suffix array containing all suffix starting positions sorted lexicographically. Once built, the answer is simply the last suffix in the sorted order because suffix arrays store suffixes in dictionary order. Building the array typically involves sorting based on ranks of prefix pairs and doubling the comparison length each round. An optional LCP (Longest Common Prefix) array helps optimize comparisons when sorting suffixes. Although asymptotically slower than the two-pointer technique, suffix arrays provide a general-purpose structure for many advanced string problems such as substring queries, pattern matching, and lexicographic ranking. Use this approach when practicing deeper string algorithms or when a reusable suffix index is needed.

Recommended for interviews: The two-pointer linear scan is the expected solution. It achieves O(n) time and constant memory while demonstrating strong reasoning about suffix comparisons. Building a suffix array proves you understand advanced string processing but is heavier than necessary for this specific problem.

Approach 1: Approach 1: Suffix Comparison Using Two Pointers

This approach uses two pointers to compare suffixes starting at different indices. Essentially, iterate over the possible starting positions using a greedy method to determine which leads to the lexicographically largest suffix.

def lastSubstring(s: str) -> str:
    i, j, k = 0, 1, 0
    n = len(s)
    while j + k < n:
        if s[i + k] == s[j + k]:
            k += 1
        elif s[i + k] < s[j + k]:
            i = max(i + k + 1, j)
            j = i + 1
            k = 0
        else:
            j = j + k + 1
            k = 0
    return s[i:]
function lastSubstring(s) {
    let i = 0, j = 1, k = 0, n = s.length;
    while (j + k < n) {
        if (s[i + k] === s[j + k]) {
            k++;
        } else if (s[i + k] < s[j + k]) {
            i = Math.max(i + k + 1, j);
            j = i + 1;
            k = 0;
        } else {
            j = j + k + 1;
            k = 0;
        }
    }
    return s.substring(i);
}

The code uses three pointers: i to mark the current best candidate for the start of the lexicographical maximum suffix, j for the current suffix start position being compared, and k for the matching process length. The loop continues until the comparison reaches the end of the string, and updates the pointers accordingly to track the maximal suffix.

Complexity

Time Complexity: O(n), where n is the length of the string since each pointer advances linearly through the string.
Space Complexity: O(1) as we are using a constant amount of extra space.

Try this approach in the editor

Approach 2: Approach 2: Suffix Array with LCP

Another approach to solving the problem involves constructing a suffix array and using the longest common prefix (LCP) array to determine the lexicographical order.

import java.util.Comparator;
import java.util.Arrays;

public class Solution {
    public String lastSubstring(String s) {
        int n = s.length();
        Integer[] suffixArray = new Integer[n];
        for (int i = 0; i < n; i++) suffixArray[i] = i;

        Arrays.sort(suffixArray, new Comparator<Integer>() {
            public int compare(Integer a, Integer b) {
                return s.substring(a).compareTo(s.substring(b));
            }
        });

        return s.substring(suffixArray[n - 1]);
    }
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

string lastSubstring(string s) {
    int n = s.size();
    vector<int> suffixArray(n);

    for (int i = 0; i < n; ++i) suffixArray[i] = i;

    sort(suffixArray.begin(), suffixArray.end(), [&s](int a, int b) {
        return s.substr(a) < s.substr(b);
    });

    return s.substr(suffixArray.back());
}

In the Java implementation, we form an integer array representing suffix indices, sort it lexicographically by comparing suffixes directly using substring operations. The resulting suffix at the final position is the desired substring.

Complexity

Time Complexity: O(n log n), primarily due to sorting.
Space Complexity: O(n), primarily to store suffix indices.

Try this approach in the editor

Approach 3: Two pointers

We notice that if a substring starts from position i, then the largest substring with the largest dictionary order must be s[i,..n-1], which is the longest suffix starting from position i. Therefore, we only need to find the largest suffix substring.

We use two pointers i and j, where pointer i points to the starting position of the current largest substring with the largest dictionary order, and pointer j points to the starting position of the current substring being considered. In addition, we use a variable k to record the current position being compared. Initially, i = 0, j=1, k=0.

Each time, we compare s[i+k] and s[j+k]:

If s[i + k] = s[j + k], it means that s[i,..i+k] and s[j,..j+k] are the same, and we add k by 1 and continue to compare s[i+k] and s[j+k];

If s[i + k] \lt s[j + k], it means that the dictionary order of s[j,..j+k] is larger. At this time, we update i = i + k + 1, and reset k to 0. If i geq j at this time, we update pointer j to i + 1, that is, j = i + 1. Here we skip all suffix substrings with s[i,..,i+k] as the starting position, because their dictionary orders are smaller than the suffix substrings with s[j,..,j+k] as the starting position.

Similarly, if s[i + k] \gt s[j + k], it means that the dictionary order of s[i,..,i+k] is larger. At this time, we update j = j + k + 1 and reset k to 0. Here we skip all suffix substrings with s[j,..,j+k] as the starting position, because their dictionary orders are smaller than the suffix substrings with s[i,..,i+k] as the starting position.

Finally, we return the suffix substring starting from i, that is, s[i,..,n-1].

The time complexity is O(n), where n is the length of string s. The space complexity is O(1).

function lastSubstring(s: string): string {
    const n = s.length;
    let i = 0;
    for (let j = 1, k = 0; j + k < n; ) {
        if (s[i + k] === s[j + k]) {
            ++k;
        } else if (s[i + k] < s[j + k]) {
            i += k + 1;
            k = 0;
            if (i >= j) {
                j = i + 1;
            }
        } else {
            j += k + 1;
            k = 0;
        }
    }
    return s.slice(i);
}
func lastSubstring(s string) string {
	i, n := 0, len(s)
	for j, k := 1, 0; j+k < n; {
		if s[i+k] == s[j+k] {
			k++
		} else if s[i+k] < s[j+k] {
			i += k + 1
			k = 0
			if i >= j {
				j = i + 1
			}
		} else {
			j += k + 1
			k = 0
		}
	}
	return s[i:]
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Approach 1: Suffix Comparison Using Two Pointers

Time Complexity: O(n), where n is the length of the string since each pointer advances linearly through the string.
Space Complexity: O(1) as we are using a constant amount of extra space.

Approach 2: Suffix Array with LCP

Time Complexity: O(n log n), primarily due to sorting.
Space Complexity: O(n), primarily to store suffix indices.

Two pointers–

Complexity analysis

ApproachTimeSpaceWhen to Use
Suffix Comparison Using Two PointersO(n)O(1)Best general solution. Interview‑preferred for large strings and optimal performance.
Suffix Array with LCPO(n log n)O(n)Useful when learning suffix arrays or when the sorted suffix structure is needed for other string queries.

Frequently asked questions

Is Last Substring in Lexicographical Order easy or hard?

LeetCode classifies this problem as Hard because the optimal O(n) solution relies on a non-obvious suffix elimination strategy. Many first attempts use brute force suffix generation or sorting, which leads to O(n^2) or O(n log n) complexity.

Last Substring in Lexicographical Order Python/Java solution

Python and JavaScript implementations usually apply the O(n) two-pointer suffix comparison method because it requires only index arithmetic and character comparisons. C++ and Java implementations sometimes demonstrate suffix array construction for educational purposes.

How to solve Last Substring in Lexicographical Order in O(n)?

Maintain two candidate suffix indices i and j along with an offset k. Compare characters s[i+k] and s[j+k]. When characters differ, discard the smaller suffix by moving its pointer forward past the compared segment and reset k. Continue until one candidate reaches the end; the remaining index marks the largest suffix.

What is the best approach for Last Substring in Lexicographical Order?

The most efficient approach uses a two-pointer suffix comparison technique. Two candidate starting indices are compared while skipping dominated suffixes, ensuring each character is processed at most a few times. This yields O(n) time and O(1) space, making it the optimal solution for large strings.

Is Last Substring in Lexicographical Order asked at Google/Amazon/Meta?

Variants of suffix comparison and lexicographically maximum substring problems appear in interviews at large tech companies including Google and Amazon. These companies often test understanding of linear-time string algorithms, suffix structures, and pointer-based comparisons.

What data structure is used in Last Substring in Lexicographical Order?

The optimal solution relies mainly on pointer indices and character comparisons rather than complex data structures. An alternative solution uses a suffix array along with an optional LCP array, which are classic structures in advanced string processing.

What is the time complexity of Last Substring in Lexicographical Order?

The optimal two-pointer algorithm runs in O(n) time with O(1) additional space. A suffix array based solution typically takes O(n log n) time and O(n) space due to sorting suffix ranks. Both methods produce the same result but the linear scan is faster.

Comments

Now write it yourselfClose this page and solve Last Substring in Lexicographical Order in the editor, without looking.
Practise Last Substring in Lexicographical Order