Leetcode 1163. Last Substring in Lexicographical Order

Problem Explanation

The problem requires finding the last substring of a given string in lexicographical order. Simply put, you are to find the 'largest' substring according to standard dictionary (lexicographical) ordering.

For instance, given the string 'abab', the coded solution should return 'bab' being the maximum lexicographical substring.

A typical approach would be to generate all the substrings, sort them in lexicographic order and return the last element but it won't be efficient due to the time and space complexity.

Solution Approach

We can solve the problem in efficient manner making use of two pointers and an offset for comparison. The core idea behind the solution approach is that we maintain two pointers, i and j, where for i < j, substring s[i:] and s[j:] are both potential answers for the problem. For each such pair (i, j), we need to compare substring s[i:] and s[j:] to find the larger one. The larger one is the potential answer.

Therefore, an iterative approach is implemented to compare characters at each index from the two pointers. If they are equal, it increments the offset k which helps to compare the next characters. If character at i + k is greater than at j + k, it implies there is a possible larger substring starting from j + k and if vice versa, we need to start from either i + k + 1 or j.

Python Solution

1
2python
3class Solution:
4    def lastSubstring(self, s: str) -> str:
5        i, j, k = 0, 1, 0
6        while j + k < len(s):
7            if s[i+k] == s[j+k]:
8                k += 1
9            elif s[i+k] > s[j+k]:
10                j = j + k + 1
11                k = 0
12            else:
13                i = max(i + k + 1, j)
14                j = i + 1
15                k = 0
16        return s[i:]

Java Solution

1
2java
3Class Solution {
4    public String lastSubstring(String s) {
5        int i = 0;
6        int j = 1;
7        int k = 0;
8        while (j + k < s.length()) {
9            if (s.charAt(i + k) == s.charAt(j + k)) {
10                ++k;
11            } else if (s.charAt(i + k) > s.charAt(j + k)) {
12                j = j + k + 1;
13                k = 0;
14            } else {
15                i = Math.max(i + k + 1, j);
16                j = i + 1;
17                k = 0;
18            }
19        }
20        return s.substring(i);
21    }
22}

JavaScript Solution

1
2javascript
3var lastSubstring = function(s) {
4    let i = 0, j = 1, k = 0;
5    while (j + k < s.length) {
6        if (s[i + k] == s[j + k]) {
7            k++;
8        } else if (s[i + k] < s[j + k]) {
9            i = j;
10            j++;
11            k = 0;
12        } else {
13            j = j + k + 1;
14            k = 0;
15        }
16    }
17    return s.slice(i);
18};

C++ Solution

1
2cpp
3class Solution {
4public:
5    string lastSubstring(string s) {
6        int i = 0, j = 1, k = 0;
7        while (j + k < s.size()) {
8            if (s[i+k] == s[j+k]) ++k;
9            else if (s[i+k] < s[j+k]) i = j, j++, k = 0;
10            else j += k+1, k = 0;
11        }
12        return s.substr(i);
13    }
14};

C# Solution

1
2csharp
3public class Solution {
4    public string LastSubstring(string s) {
5        int i = 0, j = 1, k = 0;
6        while (j + k < s.Length) {
7            if (s[i+k] == s[j+k]) ++k;
8            else if (s[i+k] < s[j+k]) i = j++, k = 0;
9            else j+= k+1, k = 0;
10        }
11        return s.Substring(i);
12    }
13}

In each of the provided solutions in above mentioned languages, we initialize the variables i, j, and k. We start a while loop where the following happens:

s[i + k] and s[j + k] comparisons denote the lexicographic order of the substrings s[i:] and s[j:]. If they are equal, we increment k and the waiting for the decision. If s[i + k] > s[j + k], s[i:] is better than s[j:], so we update j to j + k + 1 to find a better string. If s[i + k] < s[j + k], s[j:] is better than s[i:]. Here, s[i:i+k+1] can't be the start part of the result, so we can safely make i = i + k + 1 or i = j to find a better string.

The time complexity is O(n) and space complexity is O(1).# Time and Space Complexity Analysis

The class Solution provided above for this problem uses an iterative approach. As a result, the time complexity would be O(n), where n is the length of the input string s. This is because every character in the string would be visited by the algorithm at most twice. As for the space complexity, since no additional dynamic space is used throughout the computation irrespective of the input size โ€“ only a constant number of integer variables are used โ€“ the space complexity would be O(1).

The efficiency of this solution is desirable as it can handle large strings without a significant increase in execution time or memory usage, unlike the brute-force method which has a time complexity of O(n^2). This method would generate all the substrings, sort them in lexicographical order and return the highest string, thus consuming substantially more computational resources.

Key Takeaways

The solution to this problem involves understanding the key principle of lexicographical order and utilizing an efficient two-pointer approach to iterate through the input string. Using two pointers, i and j, and an offset k enables the algorithm to competently find potential answers for each pair (i, j), comparing each substring from the end of the string s.

Although the solution appears complex on the surface, it becomes straightforward when dissected into its component parts. It also showcases how a well-thought-out approach can significantly enhance computational efficiency by reducing both time and space complexity. By taking care of minor edge cases and exploiting the pattern within the problem statement, it is possible to inch closer to the optimal solution. This question underscores the importance of a clear understanding of string manipulation, lexicographical order handling and efficient use of iteration in solving competitive programming questions based around strings. Overall, it's a good problem to test and understand your control over string manipulation and optimization techniques in any programming language.

Each solution provided in Python, Java, JavaScript, C++, and C# follows identical logic, differing only in syntax. This demonstrates how similar concepts can be applied across multiple languages, thus enhancing a programmer's versatility in competitive coding environments.


Got a question?ย Ask the Teaching Assistantย anything you don't understand.

Still not clear? Ask in the Forum, ย Discordย orย Submitย the part you don't understand to our editors.

โ†
โ†‘TA ๐Ÿ‘จโ€๐Ÿซ