【1163. 按字典序排在最后的子串】

来源:力扣(LeetCode)

描述:

给你一个字符串 s ,找出它的所有子串并按字典序排列,返回排在最后的那个子串。

示例 1:

输入:s = "abab"
输出:"bab"
解释:我们可以找出 7 个子串 ["a", "ab", "aba", "abab", "b", "ba", "bab"]。按字典序排在最后的子串是 "bab"

示例 2:

输入:s = "leetcode"
输出:"tcode"

提示:

  • 1 <= s.length <= 4 * 105
  • s 仅含有小写英文字符。

方法:双指针

记字符串 s 的长度为 n。首先并非所有的子字符串都需要被考虑到,只有后缀子字符串才可能是排在最后的子字符串。

为什么只有后缀子字符串才可能是排在最后的子字符串?
考虑一个非后缀子字符串 s1,那么 s1 向后延伸得到的后缀子字符串 s2 比 s1 大,即 s2 排在 s1 后面。

我们用 si ​表示从 s[i] 开始的后缀子字符串,那么可以用指针 i 来指向后缀子字符串 s1 。我们使用指针 i 指向已知的最大后缀子字符串,j 指向待比较的后缀子字符串,初始时有 i = 0,j = 1。一个简单的做法是从小到大枚举 j,如果 si < sj,那么令 i = j,最终的后缀子字符串 si 就是排在最后的子字符串。类似于字符串匹配,在 si 与 sj 的比较过程中,一部分前缀是相等的,我们利用这一点跳过一些不需要进行比较的后缀子字符串。假设 si 与 sj 在第 k 个字符处不相等,即 si[k] ≠ sj[k],那么有两种情况:

  • si[k] < sj[k]
    • 当 i + k > j 时
      • 对于 m ∈ [1, k] 的后缀子字符串 si + m:
        • 如果 m ∈ [k − (j − i) + 1, k],显然有 si + m < sj + m,而 j + m > i + k,因此 si + m 是不需要比较的。
        • 如果 m ∈ [1, k − (j − i)],那么 si + m < sj + m = si+m+j−i ,又因为 m < m + j − i ≤ k,而 si + m 右边的后缀子字符串不需要进行比较,因此 si + m 是不需要进行比较的。

综上,下一个需要进行比较的后缀子字符串为 si+k+1
1

  • 当 i + k ≤ j 时

    • 下一个需要进行比较的后缀子字符串为 sj + 1。
  • si[k] > sj[k]

    • 对于 m ∈ [1, k] 的后缀子字符串 sj + m:
      • 如果 m ∈ [1, j − i],显然有 sj + m < si + m < si
      • 如果 m ∈ [j − i + 1, k],那么 sj + m < si + m = sj+m+i−j < si(因为 1 ≤ m + i − j < m,而 sj + m 左边的后缀子字符串小于 si,因此 sj + m 也小于 si)。

综上,对于 m ∈ [1, k],都有 sj + m < si,所以下一个需要比较的后缀子字符串为 sj+k+1

2

当 sj 为 si 的前缀子字符串,即 j + k = n 时,与情况 si[k] > sj[k] 类似。

代码:

class Solution {
public:
    string lastSubstring(string s) {
        int i = 0, j = 1, n = s.size();
        while (j < n) {
            int k = 0;
            while (j + k < n && s[i + k] == s[j + k]) {
                k++;
            }
            if (j + k < n && s[i + k] < s[j + k]) {
                int t = i;
                i = j;
                j = max(j + 1, t + k + 1);
            } else {
                j = j + k + 1;
            }
        }
        return s.substr(i, n - i);
    }
};

执行用时:28 ms, 在所有 C++ 提交中击败了95.00%的用户
内存消耗:17.9 MB, 在所有 C++ 提交中击败了96.00%的用户
复杂度分析
时间复杂度:O(n),其中 n 是字符串 s 的长度。每 k 次比较,i 与 j 共同至少向右移动 k,因此总比较次数不超过 2 × n 次。
空间复杂度:O(1)。返回值不计算空间复杂度。
author:LeetCode-Solution

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值