【问题标题】:How to iterate on a string using subscripts in swift如何在swift中使用下标迭代字符串
【发布时间】:2021-12-31 23:39:48
【问题描述】:

我来自 java 背景,String 快速切片对我来说显得非常冗长和困难。我正在尝试用一种适用于下标的算法来回答这个 leet 代码问题。

给定一个字符串s,求最长不重复字符的子串的长度。
输入:s = "abcabcbb"
输出:3
解释:答案是“abc”,长度为3。

这是我的代码:

func lengthOfLongestSubstring(_ s: String) -> Int {
 var maximum = 0
 var start = 0
 var end = 0
 var set = Set<Character>()
 while end < s.count{
     if set.contains(s[end]){
         set.remove(s[start])
         start+=1
     }
     else{
         set.insert(s[end])
         end+=1
         maximum = max(maximum, end-start)
     }
 }
}

我使用 Int 下标。但我收到此错误消息:'subscript(_:)' is unavailable: cannot subscript String with an Int, use a String.Index instead。我怎样才能解决它而不会太冗长?

【问题讨论】:

  • 如果您简单地说Array(s) 将字符串分解为字符数组,您会更轻松。现在您可以使用 Int 索引了。
  • 问题中的代码表明它不仅涉及访问第 n 个字符,还涉及迭代字符串并处理字符串中的位置。因此,我建议重新提出问题
  • @matt 对类似的字符串问题采用这种方法会有帮助吗?
  • 我知道你指的是什么。请参阅我之前的评论以获取我的回复。

标签: swift string position


【解决方案1】:

其他语言对 Unicode 字符串做了简化的假设,而 Swift 没有。您无法确定给定字符需要存储多少个代码点,因此每次索引到 unicode 字符串的第 n 个字符时,它都是一个 O(n) 操作。因此,在 Unicode 中使用随机访问整数索引的代码具有意想不到的O(n²) 性能。因此 Swift 不提供整数索引。

正如 Matt 在他的评论中所建议的,将整数索引到 Swift 字符串的字符中的简单方法是将字符串转换为字符数组。您只需为该转换支付一次 O(n) 费用(以及额外存储的内存费用),然后可以在此之后定期访问字符。

您也可以学习如何使用String.Index 为 Swift 字符串编制索引,但这需要您更改当前的方法。

【讨论】:

  • 除此之外,重要的是要注意将String 转换为Array&lt;Character&gt;O(n) 副本,这是可以避免的。许多字符串操作算法需要整数索引,即使算法用整数索引表示,如果它不需要真正随机访问字符串,它也可以用术语表示的String 切片操作 - 这些不需要副本。像dropFirst(_:) 这样的下标和函数是你的朋友。
  • 没错,转换一个字符串需要O(n)时间,一次。但是,这可以防止您意外创建 O(n²) 性能,因为对于每个操作,Unicode 字符串的整数索引将在 O(n) 时间内运行。
  • 我编辑了我的答案,以明确将字符串转换为数组具有一次性 O(n) 成本这一事实。
  • 是的!我认为这是一个很大的进步。我主要想在我的评论中表达,许多刚接触 Swift 的人都使用基于整数的索引,因为他们已经习惯了其他语言,并且在许多(大多数? ) 情况,但这确实有助于澄清。
  • @ItaiFerber 我完全同意。这就是为什么我在评论中说学习如何使用字符串是更好的解决方案。我只是想给 OP 一种前进的方式,而无需这样做。当 OP 询问总体上这是否是一个好主意时,我表示反对,原因正是您给出的原因。
【解决方案2】:

为什么访问字符串中的字符这么难?

如果字符串由存储在数组中的固定大小的字符组成,则使用整数下标查找第 n 个字符将是直接且高效的。

但流行的字符串编码(例如 UTF-8 和 UTF-16)使用可变数量的字节/字来存储字符。因此,直接访问第 n 个字符需要从头开始逐个计数字符或其他昂贵的策略,以正确处理 "Abçd??efg" 等字符串(9 个字符,但 11 个 UTF16 字和 16 个 UTF8 字节)

快速修复

由于下标需要索引并且您使用整数,您可以通过将错误的s[i] 替换为:

s[s.index(s.startIndex, offsetBy: i))]

这不是很简洁。但它的优点是可以将您的注意力吸引到整数字符数的计算和索引的复杂性上。 (如果您要分析使用非常长的字符串的代码,您会很快发现这是一个瓶颈)。

如何更好地利用索引?

另一方面,许多算法(包括您的算法)一个接一个地访问字符。因此,如果您使用相对位置,则无需从头开始一遍又一遍地重新计算字符。出于这个原因,Swift 设计者选择使用索引为字符串下标。

因此,更好的方法是使用 Swift 的原生方式:使用索引而不是整数并遍历字符串:

func lengthOfLongestSubstring(_ s: String) -> Int {
    var maximum = 0
    var start = s.startIndex   // starting at start of the string
    var end = s.startIndex
    var set = Set<Character>()
    while end != s.endIndex{  // is end of string reached ? 
        if set.contains(s[end]){
            set.remove(s[start])
            start = s.index(after: start)  // going to the next character
        }
        else{
            set.insert(s[end])
            end = s.index(after:end)    // going to the next character
            maximum = max(maximum, s.distance(from: start, to:end)) // count characters between indexes
        }
    }
    return maximum
}

这里是quick introduction,关于有用的索引功能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-15
    • 2016-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多