1163. Last Substring in Lexicographical Order

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

 

Example 1:

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

Example 2:

Input: "leetcode"
Output: "tcode"

 

Note:

  1. 1 <= s.length <= 4 * 10^5
  2. s contains only lowercase English letters.

解题思路:

因为s.substr(i , j) 的字典序一定小于s.substr(i);所以问题转换成求s.substr(i)(0 <= i < len)的字典序最大的字符串;一共有n个子串,如果每个字符串都比较找出字典序最大的字符串则时间复杂度为O(n^2),一定会超时。因为两个字符串比较的时间为O(n),可以通过将字符串比较的时间降为O(1),这样时间复杂度就为O(n).

将字符串比较时间降为O(1): 我想用旋转哈希表来做,求每个字符串的哈希值 ;通过旋转哈希表,可以使求每个字符串的哈希值的时间复杂度降为O(1); 将每个子字符串后缀添'a'使其长度与s的长度一样长,求其哈希值,哈希值最大的那个子字符串就是字字典序最大的字符串;看起来没什么问题,但是s的长度可达到10^5, 那么每个子字符串的哈希值会大到无法想象,解决办法是取模,但是取模之后就无法保证哈希值最大的子字符串就是子字典序最大的字符串了;

class Solution {
public:
    string lastSubstring(string s) 
    {
        if(s.empty()) return "" ;
        
        int modl = 1000000007 , len = s.size();
        long base = 1 ;
        for(int i = 1 ; i < len ; i++) base = (base * 26) % modl;
        
        map<int , vector<int>> hash_poses ; 
        long prev_hash = 0 ;
        
        for(int i = len - 1 ; i >= 0 ; i--)
        {
            int hash = (prev_hash / 26 + ((s[i] - 'a') * base) % modl ) % modl;
            
            hash_poses[hash].push_back(i) ;
            prev_hash = hash ;
        }
        
        auto iter_max_hash = prev(hash_poses.end() , 1) ;
        
        vector<int>& poses = iter_max_hash->second ;
        
        string res = s.substr(poses[0]) ;
        
        for(int i = 1 ; i < poses.size() ; i++)
        {
            string tmp = s.substr(poses[i]) ;
            if(strcmp(tmp.c_str() , res.c_str()) > 0)
                res = tmp ;
        }
        
        return res ;

    }
};

 

方法二:

从头遍历s,如果s[index] > s[max_index],那么以s[index]开头的字符串字典序一定大于以s[max_index]开头的字符串;

如果s[index] == s[max_index]

//记录index , max_index

int begin_index = index , max_pos = max_index ;

//使index和max_pos继续后移

while(index < len && max_pos < begin_index && s[index] == s[max_pos])
 {
        index++ ;
        max_pos++;

}

增加max_pos < begin_index条件的原因是:

如果max_pos == begin_index,说明s.substr(max_index) 和 s.substr(begin_index)有相同的长度为(begin_index - max_index)的前缀,设其相同前缀为x,剩余的字符串为y; 那么s.substr(max_index) = x + x + y , s.substr(begin_index) = x + y ; 如果 x < y , 那么 x + x +y < x + y < y , 字典序最大的字符串为y ; 如果 x > y , 那么 x + x + y > x + y > y , 字典序最大的字符串为x +x +y ; 所以只要使 x 和 y 比较就好了 , 也就是使s[max_index] 和 s[index]比较 ;

class Solution {
public:
    string lastSubstring(string s) 
    {
        int max_index = 0 , index = 1 , len = s.size() ;
        
        while(index < len)
        {
            if(s[index] > s[max_index])
            {
                max_index = index;
                index++ ;
            }
            else if(s[index] == s[max_index])
            {
                int begin_index = index , max_pos = max_index ;
                
                while(index < len && max_pos < begin_index && s[index] == s[max_pos])
                {
                    index++ ;
                    max_pos++ ;
                }
                
                if(index >= len || max_pos >= begin_index) continue ;
                
                if(s[index] > s[max_pos])
                    max_index = begin_index ;
            }
            else index++ ;
        }
        
        return s.substr(max_index) ;
    }
};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值