K距離間隔重排字符串 (LeetCode 358) — Swift解法 + 可運行Demo

在這里插入圖片描述
在這里插入圖片描述

文章目錄

    • 摘要
    • 描述
    • 解決方法
    • 分析問題和解決代碼
      • 代碼要點詳解
    • 示例測試和結果
    • 時間復雜度
    • 空間復雜度
    • 總結

摘要

這道題的核心是:把字符串里的字符重新排一下順序,讓相同字符之間至少隔開 k 個位置。如果做不到,就返回空串。看上去像“排座位”,其實是一道標準的“貪心 + 堆 + 冷卻隊列”題。本文用 Swift 寫出一份可直接運行的 Demo,順便把實現細節和常見坑講清楚。

描述

給定一個字符串 s 和整數 k,要求把 s 里的字符重新排列,使得任意兩個相同字符在新字符串中的位置至少相隔 k

  • 如果存在這樣的重排,返回重排后的字符串。
  • 如果不存在,返回 ""

直覺:頻率高的字符更“難安置”,理應優先被安排;但又不能連續塞,必須“冷卻”至少 k-1 個位置。于是我們需要:

  1. 一個**最大堆(優先隊列)**按剩余次數挑選字符。
  2. 一個冷卻隊列存放剛剛用過的字符,等走過 k 步再重新投入堆。

解決方法

思路分三步:

  1. 統計每個字符的出現次數。

  2. 用最大堆按“剩余次數”挑一個當前最應該放的字符。

  3. 將每輪最多取 k 個不同字符依次放入結果,同時把它們的剩余次數減一并放入“等待區”。當這輪結束,再把等待區里還剩次數的字符放回堆,進入下一輪。

    • 如果某輪拿不到足夠的不同字符,但還有字符沒用完,說明無法滿足間隔要求,直接返回 ""

這個分輪的方式,簡單直觀,也好寫對。

分析問題和解決代碼

下面是完整的 Swift 實現,包含一個通用的最大堆(優先隊列)實現、解題函數,以及可運行的測試 Demo。

import Foundation// MARK: - Generic Max Heap
public struct PriorityQueue<Element> {private var elements: [Element] = []private let areSorted: (Element, Element) -> Bool  // max-heap: return lhs > rhspublic init(sort: @escaping (Element, Element) -> Bool) {self.areSorted = sort}public var isEmpty: Bool { elements.isEmpty }public var count: Int { elements.count }public mutating func push(_ value: Element) {elements.append(value)siftUp(from: elements.count - 1)}public mutating func pop() -> Element? {guard !elements.isEmpty else { return nil }elements.swapAt(0, elements.count - 1)let item = elements.removeLast()siftDown(from: 0)return item}public func peek() -> Element? {elements.first}private mutating func siftUp(from index: Int) {var child = indexvar parent = (child - 1) / 2while child > 0 && areSorted(elements[child], elements[parent]) {elements.swapAt(child, parent)child = parentparent = (child - 1) / 2}}private mutating func siftDown(from index: Int) {var parent = indexwhile true {let left = parent * 2 + 1let right = left + 1var candidate = parentif left < elements.count && areSorted(elements[left], elements[candidate]) {candidate = left}if right < elements.count && areSorted(elements[right], elements[candidate]) {candidate = right}if candidate == parent { return }elements.swapAt(parent, candidate)parent = candidate}}
}// MARK: - Solution
class Solution {/// Rearrange string so that same chars are at least k distance apart./// - Returns: rearranged string or "" if impossiblefunc rearrangeString(_ s: String, _ k: Int) -> String {// 快速返回:k <= 1 不需要限制if k <= 1 { return s }if s.isEmpty { return "" }// 1) 統計頻率var freq = [Character: Int]()for ch in s {freq[ch, default: 0] += 1}// 2) 最大堆,按剩余次數排序(次數相同可按字母排序,方便穩定)// 元組:(char, count)var maxHeap = PriorityQueue<(Character, Int)>(sort: { (a, b) inif a.1 == b.1 {return a.0 < b.0  // 次數相等時,字典序更小的優先(不必須,但穩定)}return a.1 > b.1})for (ch, c) in freq {maxHeap.push((ch, c))}// 3) 分輪放置,每輪最多放 k 個不同字符var result: [Character] = []// 臨時存放本輪用過的字符(減1后如果還有余量,等輪末再壓回堆)while !maxHeap.isEmpty {var usedInThisRound: [(Character, Int)] = []var take = min(k, s.count - result.count) // 剩余位數可能不足 kfor _ in 0..<take {guard let (ch, c) = maxHeap.pop() else {// 堆空但還沒填滿(說明還有待放字符被卡住),失敗// 注意:只有當結果未完成且堆空時,才是失敗return ""}result.append(ch)if c - 1 > 0 {usedInThisRound.append((ch, c - 1))}// 若已經填滿,提前結束if result.count == s.count { break }}// 輪末:把本輪用過仍有余量的字符壓回堆for item in usedInThisRound {maxHeap.push(item)}// 如果堆仍有元素,但是這一輪沒湊夠 k 個且結果還沒填滿,說明必須留空位才能滿足距離要求,但字符串不允許空位 => 不可能if !maxHeap.isEmpty && result.count < s.count && usedInThisRound.count < k {// 這個判斷等價:本輪拿的不同字符數 < k,但后面還有字符沒安排// 說明“冷卻距離”要求擋住了return ""}}return String(result)}
}// MARK: - Demo (Runnable)
func demo() {let sol = Solution()let cases: [(String, Int)] = [("aabbcc", 3),     // 可行,典型示例("aaabc", 3),      // 不可行,返回 ""("aaadbbcc", 2),   // 可行("a", 2),          // 只有一個字符,k>1 也可行(本身就滿足)("", 2),           // 空串("aa", 1),         // k=1 總是原樣即可("aa", 2),         // 需要至少間隔 2,兩個 a 無法滿足 => ""]for (s, k) in cases {let ans = sol.rearrangeString(s, k)print("Input: s=\(s), k=\(k) -> Output: \(ans.isEmpty ? "\"\"" : ans)")}
}// Run demo
demo()

代碼要點詳解

  1. 最大堆設計

    • 我們用泛型 PriorityQueue,構造時傳 sort 比較函數,讓它成為“最大堆”。
    • 堆里的元素是 (Character, Int),其中 Int 是剩余頻次,按頻次降序;為了穩定性,頻次相同按字符字典序小的優先(不必須,僅讓結果可預期一點)。
  2. 分輪取 k 個不同字符

    • 每一輪最多取 k 個不同字符,確保相同字符之間天然相隔至少 k
    • 本輪用過的字符,頻次減一后暫存到 usedInThisRound,等這一輪結束再放回堆,避免本輪再次被取到。
  3. 失敗條件

    • 如果堆提前空了但還沒填滿字符串,說明被“冷卻”規則卡住了,不可行。
    • 或者本輪沒拿滿 k 個(意味著后面位置距離不夠了)且堆里還有字符,也是不可能的情況,返回空串。
  4. 為什么不是用“時間戳冷卻”

    • 另一種寫法是給每個字符打“readyTime”,當 i >= readyTime 才能重新入堆。這也行,但分輪更直觀:一輪拿 k 個不同字符,本輪結束再回堆,自然保證間隔。

示例測試和結果

運行上面的 demo(),輸出大致如下(具體順序可能因為同頻字符的 tie-breaker 有細微差別,但滿足約束即可):

Input: s=aabbcc, k=3 -> Output: abcabc
Input: s=aaabc, k=3 -> Output: ""
Input: s=aaadbbcc, k=2 -> Output: abacabcd
Input: s=a, k=2 -> Output: a
Input: s=, k=2 -> Output: ""
Input: s=aa, k=1 -> Output: aa
Input: s=aa, k=2 -> Output: ""

簡單解釋:

  • aabbcck=3:可以按 abcabc 擺,符合任意同字母之間至少隔 3。
  • aaabck=3a 太多,中間插不進足夠的“墊片”,失敗。
  • aaadbbcck=2:可以,比如 abacabcd 就行。
  • 單字符或 k=1 都是“沒有硬性間隔限制”,基本能過。

時間復雜度

  • 設字符串長度為 n,不同字符種類數為 σ(最多 26 個小寫,或 128/256 ASCII)。
  • 每個字符都會被插入/彈出堆多次(與它的頻次成正比),堆操作是 O(log σ)
  • 總體復雜度:O(n log σ)。在英文小寫場景基本可視為 O(n)

空間復雜度

  • 頻率表 O(σ),堆 O(σ),結果 O(n)
  • 額外空間:O(n + σ),在字符集有限時接近 O(n)

總結

這題看似是“重排字符串”,其實就是優先安排最難放的字符,再用冷卻隊列保證間隔。實現上:

  • 最大堆挑剩余次數最多的字符;
  • 分輪放置,每輪最多 k 個不同字符,輪末再把有余量的字符放回堆;
  • 如果某輪拿不到 k 個,但還有字符沒放完,說明不可能。

這種模式在很多“調度/任務安排”的題里都能復用,比如 CPU 任務調度、訂單/工位分配等。寫成 Swift 后,借助一個干凈的優先隊列模板,整件事就順了。歡迎把這份 Demo 拿去直接跑,也可以按你業務里的字符集和限制微調。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93992.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93992.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93992.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

React native Navigation 詳解

Tab Navigator(標簽導航器) 概念 Tab Navigator 是 React Navigation 中用于創建底部或頂部標簽欄導航的組件。它允許用戶在不同的屏幕之間快速切換,每個標簽對應一個獨立的屏幕。 基本用法 import {createBottomTabNavigator } from @react-navigation/bottom-tabs; im…

[GraphRAG]完全自動化處理任何文檔為向量知識圖譜:AbutionGraph如何讓知識自動“活”起來?

在當今信息爆炸的時代&#xff0c;企業和研究人員面對大量非結構化文檔時&#xff0c;如何高效地提取、存儲和查詢其中的知識&#xff0c;已成為一個核心挑戰。傳統的關鍵詞檢索早已無法滿足深層次語義關聯和智能問答的需求。 每天面對成百上千份PDF論文、Excel報告、行業白皮…

模擬tomcat接收GET、POST請求

訪問&#xff1a; http://localhost:10086/mytomcatMyTomcat/ └── src/└── com/└── zhang/├── MyServer.java├── MyRequest.java├── MyResponse.java├── MyMapping.java├── MyServlet.java└── MyHttpServlet.java核心類功能說明 MyServer.java 服務…

氯化釔:科技與高性能材料的核心元素

氯化釔是釔元素的氯化物&#xff0c;廣泛應用于高性能材料、催化劑、光電技術等領域。作為稀土元素之一&#xff0c;釔因其獨特的物理和化學特性&#xff0c;在現代工業中具有重要地位&#xff0c;而氯化釔則是其中的關鍵化合物之一。氯化釔的優勢與特點1. 化學穩定性強氯化釔具…

【數據結構初階】--排序(五):計數排序,排序算法復雜度對比和穩定性分析

&#x1f618;個人主頁&#xff1a;Cx330? &#x1f440;個人簡介&#xff1a;一個正在努力奮斗逆天改命的二本覺悟生 &#x1f4d6;個人專欄&#xff1a;《C語言》《LeetCode刷題集》《數據結構-初階》 前言&#xff1a;今天這篇博客就給大家將一個計數排序&#xff0c;然乎就…

Incredibuild 新增 Unity 支持:擊破構建時間過長的痛點

任何開發過復雜 Unity 項目的團隊都會告訴你&#xff1a;構建速度已成為生產流程中的核心痛點。Unity 靈活且強大&#xff0c;但隨著項目規模擴大&#xff08;尤其是包含 3D 資源、復雜著色器和龐大內容管線的項目&#xff09;&#xff0c;構建過程會逐漸變成一項隱性成本。 多…

大數據接口 - 收入評估(社保評級)API

請求端點 {"post": "https://api.tianyuanapi.com/api/v1/JRZQ09J8?t13位時間戳" }請求頭字段名類型必填描述Access-Idstring是賬號的 Access-Id對于業務請求參數 通過加密后得到 Base64 字符串&#xff0c;將其放入到請求體中&#xff0c;字段名為 data&…

C++八股 —— 設計模式

文章目錄一、創建型模式1. 單例模式2. 工廠模式二、結構型模式1. 裝飾器模式2. 代理模式三、行為型模式1. 觀察者模式2. 策略模式一、創建型模式 1. 單例模式 C八股 —— 單例模式_c 單例模式-CSDN博客 2. 工廠模式 參考&#xff1a;【設計模式】工廠模式詳解-----簡單工廠…

在openeuler中如何使用 firewalld 開放指定端口

在 OpenEuler 中使用 firewalld 開放指定端口的操作步驟如下&#xff0c;需區分臨時開放&#xff08;重啟后失效&#xff09;和永久開放&#xff08;重啟后保留&#xff09;兩種場景&#xff1a;一、查詢端口當前狀態首先確認端口是否已開放&#xff0c;避免重復配置&#xff1…

【Java進階】Java JIT 編譯器深度解析與優化實踐

Java JIT 編譯器深度解析與優化實踐Java JIT 編譯器深度解析與優化實踐一、JIT 編譯器核心原理1. JIT 工作流程2. 熱點代碼檢測機制二、Java 8 JIT 優化升級1. 分層編譯優化2. 方法內聯增強3. 循環優化升級4. 逃逸分析增強5. 向量化支持三、JIT友好代碼設計原則1. 方法設計優化…

【本地部署問答軟件Apache Answer】Answer開源平臺搭建:cpolar內網穿透服務助力全球用戶社區構建

文章目錄前言1. 本地安裝Docker2. 本地部署Apache Answer2.1 設置語言選擇簡體中文2.2 配置數據庫2.3 創建配置文件2.4 填寫基本信息3. 如何使用Apache Answer3.1 后臺管理3.2 提問與回答3.3 查看主頁回答情況4. 公網遠程訪問本地 Apache Answer4.1 內網穿透工具安裝4.2 創建遠…

華為數通認證學習

1、華為人才認證官網&#xff0c;https://e.huawei.com/cn/talent/portal/#/ 很全面的網站&#xff0c;包含了概述、了解認證、參加考試、學習資源、認證資訊四個板塊。可以了解華為認證的整個流程、下載學習資源&#xff08;培訓教材、視頻課程等&#xff09;&#xff0c;以及…

Android-ContentProvider的跨應用通信學習總結

一、ContentProvider的概念1. ContentProvider 是什么&#xff1f;&#xff08;核心概念&#xff09;ContentProvider 是 Android 四大組件之一。它的核心職責是管理和共享應用的結構化數據。我們可以把它想象成一個應用的**“數據大使館”**。在一個國家里&#xff08;Android…

Java數據結構第二十六期:解密位圖,海量數據處理的 “空間魔法”

專欄&#xff1a;Java數據結構秘籍 個人主頁&#xff1a;手握風云 目錄 一、位圖 1.1. 概念 1.2. 面試題 1.3. 位圖的實現 1.4. 位圖的應用 一、位圖 1.1. 概念 在數據結構中&#xff0c;位圖&#xff08;也稱為位數組、位向量或位集&#xff09;是一種緊湊的方式來表示一…

芯科科技即將重磅亮相IOTE 2025深圳物聯網展,以全面的無線技術及生態覆蓋賦能萬物智聯

作為低功耗無線連接領域的創新性領導廠商&#xff0c;Silicon Labs&#xff08;亦稱“芯科科技”&#xff09;將于8月27至29日攜其最前沿的人工智能&#xff08;AI&#xff09;和物聯網&#xff08;IoT&#xff09;解決方案在深圳舉辦的IOTE 2025國際物聯網展中盛大展出。這場亞…

Linux上安裝多個JDK版本,需要配置環境變量嗎

簡短回答&#xff1a;不需要同時配置多個 JDK 的 JAVA_HOME 和 PATH&#xff0c;但你可以安裝多個版本&#xff0c;并通過靈活的方式在它們之間切換。 文章目錄? 正確做法&#xff1a;安裝多個 JDK&#xff0c;但只讓一個生效&#xff08;通過環境變量或 alternatives&#xf…

MySQL有哪些高可用方案

大家好&#xff0c;我是鋒哥。今天分享關于【MySQL有哪些高可用方案】面試題。希望對大家有幫助&#xff1b; MySQL有哪些高可用方案? 超硬核AI學習資料&#xff0c;現在永久免費了&#xff01; MySQL 高可用方案是指確保 MySQL 數據庫在面對硬件故障、網絡故障、負載過重等…

【Windows】Windows平臺基于加速地址安裝vcpkg并集成到Visual Studio 2017

基礎運行環境 啟動&#xff1a; 適用于 VS 2017 的 x64 本機工具命令提示 ninja 下載壓縮包 https://gh-proxy.com/https:/github.com/ninja-build/ninja/releases/download/v1.13.1/ninja-win.zip 直接解壓到c:/Windows (無需配置環境變量) CMake 下載安裝包 https://gh-proxy…

LLMs之MCP:Chrome MCP的簡介、安裝和使用方法、案例應用之詳細攻略

LLMs之MCP&#xff1a;Chrome MCP的簡介、安裝和使用方法、案例應用之詳細攻略 目錄 Chrome MCP的簡介 1、特點 2、與類似項目的比較 Chrome MCP的安裝和使用方法 1、安裝 2、使用方法 加載 Chrome 擴展 與 MCP 協議客戶端一起使用 使用 STDIO 連接&#xff08;替代方…

【Java EE】多線程-初階 synchronized 關鍵字 - 監視器鎖 monitor lock

synchronized 關鍵字 - 監視器鎖 monitor lock5. synchronized 關鍵字 - 監視器鎖 monitor lock5.1 synchronized 的特性5.2 synchronized 使??例5.3 Java 標準庫中的線程安全類本節?標? 掌握 synchronized關鍵字5. synchronized 關鍵字 - 監視器鎖 monitor lock &#xf…