1163. Last Substring in Lexicographical Order

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.

题目:给定一个字符串,输出最大子字符串。

思路:可以分析得到,最大子字符串一定是此字符串的一个后缀,难点在找字符串的起始点。用两个指针,一个start记录当前最大的起始点,一个i往前遍历找下一个起始点。时间复杂度是难点。分三种情况:

        1)如果s[i] < s[start], 很简单,直接往前走就好了i++

        2) 如果s[i] > s[start], 也很简单,直接更新start = i;

        3) 难点在如果s[i] == s[start]怎么确定两个子字符串哪个大。可以直接比较子字符串大小,但时间复杂度太大了。只能一个一个往后遍历,用两个指针fast和slow将相等的地方都跳过去。这里也分三种情况:

  •   fast先到字符串结尾了,则start, slow指针遍历的子字符串肯定是最大,直接返回
  •   s[fast] < s[slow], 则start位置不动,原子字符串最大,遍历过的地方不需再遍历,更新i值
  •  s[fast] > s[slow], 则start也需要更新到i, 遍历过的地方不需要再遍历,更新i值。

这里有些不好理解,为什么遍历过的地方不需再遍历了。在跳过相等位置的时候有几种情况:

       1)相等的字符串很短 ,比i和start距离短,例如zxyabcdzxybcda,zxya...zxyb..., 则其实z后的x和y已经被i遍历过了,也与z比较过了,因此可以肯定的是比z小,指针可以安全的跳到第二个z处和b处。如果是前一字符串大,则start不变,指针i直接跳到fast之后即可。后一字符串大,指针start更新,i需要往后挪一位。

        2)相等的字符串很长,比i和start的距离长,例如zabcdzabcdzac, 中间相等的地方可以证明是相同的字符串(具体说是start与i之间的字符串)不停循环组成。如果是前一字符串大,则不需要更新start,将指针i更新到fast之后(因为fast之前肯定不会有比当前字符大的)。如果是后一字符串大,就比较复杂了,说明fast肯定跳到新一轮循环中,而且新一轮循环的字符串不是全部,中间有一个字符变大了,我们没办法确定大的字符有多大,因此指针需要移到这个循环的开始位置重新比较。可以肯定当前slow指针肯定在循环中,为使代码兼容且简洁,指针i移到slow之后。

        代码中s[fast] > s[slow]的i指针用 "i=max(i, slow),i++" 来更新,i = i+1即为上述第一种情况时,i = slow+1即为第二种情况。

代码:

class Solution {
public:
    string lastSubstring(string s) {
        int start = 0, i = 1;
        while(i < s.length()){
            if(s[i] > s[start]) start = i;
            else if(s[i] == s[start]){
                int slow = start, fast = i;
                while(fast < s.length() && s[slow] == s[fast]){
                    slow++;
                    fast++;
                }
                if(fast == s.length()) return s.substr(start);
                else if(s[fast] > s[slow]) {
                    start = i; 
                    i = max(i, slow);
                } else i = fast;
            }
            i++;
        }
        return s.substr(start);
    }
};

time: O(N), space:O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值