zoukankan      html  css  js  c++  java
  • 字符串-串的最大表示-后缀数组-1163. 按字典序排在最后的子串

    2020-03-13 13:48:05

    问题描述:

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

    示例 1:

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

    示例 2:

    输入:"leetcode"
    输出:"tcode"

    提示:

    1 <= s.length <= 4 * 10^5
    s 仅含有小写英文字符。

    问题求解:

    首先看数据规模,暴力求解是肯定不行的。时间复杂度基本在O(n) / O(nlogn)级别可以通过。

    本题有个点非常重要就是最大的子串一定是其后缀,试想如果最大的子串不是后缀,那么往后追加字符必然比它本身大,矛盾。

    但是仅仅观察到这一步暴力求解依然只能作出O(n ^ 2)的解,如何更进一步呢?

    想到后缀自然就想到后缀数组,如果使用倍增法,可以在O(nlogn)得到解,如果使用DC3/SA-IS可以在O(n)得到解,可行。

    另外,本题还可以转化为字符串的最大表示,本质上是一样的。

    简单的来说,字符串的最大表示和最小表示类似,就是遇到小的直接跳过即可,采用这种算法可以在O(n)的时间复杂度得到解。

    方法一:字符串的最大表示

    时间复杂度:O(n)

      public String lastSubstring(String s) {
            int n = s.length();
            int i = 0; 
            int j = 1;
            int k = 0;
            while (i < n && j < n && k < n) {
                int diff = s.charAt((i + k) % n) - s.charAt((j + k) % n);
                if (diff == 0) k++;
                else {
                    if (diff > 0) j = j + k + 1;
                    else i = i + k + 1;
                    k = 0;
                    if (i == j) j++;
                }
            }
            return s.substring(Math.min(i, j), n);
        }

    方法二:后缀数组-倍增

    时间复杂度:O(nlogn)

        public String lastSubstring(String s) {
            int n = s.length();
            Integer[] sa = new Integer[n + 1];
            int[] rank = new int[n + 1];
            for (int i = 0; i <= n; i++) {
                sa[i] = i;
                rank[i] = i == n ? -1 : s.charAt(i);
            }
            for (int k = 1; k < n; k *= 2) {
                int offset = k;
                Arrays.sort(sa, new Comparator<Integer>(){
                    public int compare(Integer o1, Integer o2) {
                        if (rank[o1] != rank[o2]) return Integer.compare(rank[o1], rank[o2]);
                        else {
                            int r1 = o1 + offset <= n ? rank[o1 + offset] : -1;
                            int r2 = o2 + offset <= n ? rank[o2 + offset] : -1;
                            return Integer.compare(r1, r2);
                        }
                    }
                });
                int[] new_rank = new int[n + 1];
                new_rank[sa[0]] = 0;
                for (int i = 1; i <= n; i++) {
                    new_rank[sa[i]] = new_rank[sa[i - 1]];
                    if (!isEqual(rank, sa[i], sa[i - 1], k)) new_rank[sa[i]]++;
                }
                for (int i = 0; i <= n; i++) {
                    rank[i] = new_rank[i];
                }
            }
            return s.substring(sa[n]);
        }
        
        boolean isEqual(int[] rank, int i, int j, int k) {
            int n = rank.length;
            if (rank[i] != rank[j]) return false;
            int r1 = i + k <= n ? rank[i + k] : -1;
            int r2 = j + k <= n ? rank[j + k] : -1;
            return r1 == r2;
        }
    

      

  • 相关阅读:
    URL编码和Bsae64编码
    在控制台保存下载数据文件方法
    前端下载文件的几种方式
    npm i error:0909006C:PEM routines:get_name:no start line 遇到问题解决
    MapTalks.js 使用小结(三): 各类地图加载
    在线瓦片地图服务资源 总结
    npm webpack 遇到的问题解决方案总结
    MapTalks.js 使用小结(二)
    可拖动的进度条 in vue
    滚动条vue-seamless-scroll的使用
  • 原文地址:https://www.cnblogs.com/hyserendipity/p/12486086.html
Copyright © 2011-2022 走看看