关于后端:最小表示法模板级运用的困难题

5次阅读

共计 2074 个字符,预计需要花费 6 分钟才能阅读完成。

题目形容

这是 LeetCode 上的 899. 有序队列 ,难度为 艰难

Tag :「结构」、「最小表示法」

给定一个字符串 s 和一个整数 k。你能够从 s 的前 k 个字母中抉择一个,并把它加到字符串的开端。

返回 在利用上述步骤的任意数量的挪动后,字典上最小的字符串。

示例 1:

输出:s = "cba", k = 1

输入:"acb"

解释:在第一步中,咱们将第一个字符(“c”)挪动到最初,取得字符串“bac”。在第二步中,咱们将第一个字符(“b”)挪动到最初,取得最终后果“acb”。

示例 2:

输出:s = "baaca", k = 3

输入:"aaabc"

解释:在第一步中,咱们将第一个字符(“b”)挪动到最初,取得字符串“aacab”。在第二步中,咱们将第三个字符(“c”)挪动到最初,取得最终后果“aaabc”。

提醒:

  • $1 <= k <= S.length <= 1000$
  • s 只由小写字母组成。

最小表示法

当 $k > 1$ 时,咱们可能结构出任意的字符串计划,因而当 $k > 1$ 时,咱们能够间接通过对字符串排序来失去答案,复杂度为 $O(n\log{n})$。

当 $k = 1$ 时,咱们共有 $n$ 种候选计划(将字符串 s 看作一个首尾相接的循环字符串,共有 $n$ 个终点可枚举),枚举过程中须要与以后最优的计划进行比拟,比拟复杂度为 $O(n)$,因而整体复杂度为 $O(n^2)$。

上述的做法曾经能够通过本题,能够看出瓶颈在于对 $k = 1$ 的解决。

而实际上,对于给定字符串 s,求其循环同构的所有计划中字典序最小的计划,能够应用「最小表示法」来做,复杂度为 $O(n)$。

最小表示法将「计划比拟」与「结构更优计划」进行联合:假如咱们以后有两字符串 ab 须要进行比拟,其均为原串 s 的循环同构具体计划。假如 ab 别离对应了原串下标为 ij 的具体计划,且假如两字符串前 $k$ 个字符均雷同。

当两字符串第一个不同的字符大小关系为 $cs[i + k] > cs[j + k]$ 时,能够发现在下标范畴 $idx \in [i, i + k]$ 作为终点的新计划 a' 必然不会是最优计划,即必然存在下标范畴 $idx – i + j$ 作为终点的新计划 b' 比其更优,因而咱们能够间接从 $i + k + 1$ 地位结构新的更优计划,并与 b 再次比拟。而 $cs[i + k] < cs[j + k]$ 的剖析同理。

更为直白的表述为:别离从 ij 作为终点的字符串 ab,其前 $k$ 个字符雷同,而当 $cs[i + k] > cs[j + k]$ 时,咱们能够明确「以 $i + p$ 为终点的字符串」必不可能比「以 $j + p$ 为终点的字符串」更优,其中 $p \in [0, k]$。

Java 代码:

class Solution {public String orderlyQueue(String s, int _k) {char[] cs = s.toCharArray();
        if (_k == 1) {
            int i = 0, j = 1, k = 0, n = cs.length;
            while (i < n && j < n && k < n) {char a = cs[(i + k) % n], b = cs[(j + k) % n];
                if (a == b) k++;
                else {if (a > b) i += k + 1;
                    else j += k + 1;
                    if (i == j) i++;
                    k = 0;
                }
            }
            i = Math.min(i, j);
            return s.substring(i) + s.substring(0, i);
        } else {Arrays.sort(cs);
            return String.valueOf(cs);
        }
    }
}

TypeScript 代码:

function orderlyQueue(s: string, _k: number): string {if (_k == 1) {
        let i = 0, j = 1, k = 0, n = s.length
        while (i < n && j < n && k < n) {const a = s[(i + k) % n], b = s[(j + k) % n]
            if (a == b) k++;
            else {if (a > b) i += k + 1
                else j += k + 1
                if (i == j) i++
                k = 0
            }
        }
        i = Math.min(i, j)
        return s.substring(i) + s.substring(0, i)
    } else {return [...s].sort().join('');
    }
};
  • 工夫复杂度:当 $k = 1$ 时,复杂度为 $O(n)$;当 $k > 1$ 时,复杂度为 $O(n\log{n})$
  • 空间复杂度:当 $k > 1$ 时,须要应用额定的排序空间 $O(\log{n})$

最初

这是咱们「刷穿 LeetCode」系列文章的第 No.899 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,局部是有锁题,咱们将先把所有不带锁的题目刷完。

在这个系列文章外面,除了解说解题思路以外,还会尽可能给出最为简洁的代码。如果波及通解还会相应的代码模板。

为了不便各位同学可能电脑上进行调试和提交代码,我建设了相干的仓库:https://github.com/SharingSou…。

在仓库地址里,你能够看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其余优选题解。

更多更全更热门的「口试 / 面试」相干材料可拜访排版精美的 合集新基地 🎉🎉

本文由 mdnice 多平台公布

正文完
 0