String Indices - Value of type 'Int' has no subscripts

I'm encountering this error "Value of type 'Int' has no subscripts" in this code.
Could someone please help me understand how to work with String indices.
Thank you in advance for your help!

func longestCommonPrefix(_ strs: [String]) -> [String] {

      var result = [String]()

    for i in 0..<strs.count {
        let stringIndex = strs.index(strs.startIndex, offsetBy: i)
        if stringIndex[0] == stringIndex[1] {

        }
      }
    return result
  } 

I’ve moved this thread from Development: Swift Syntax to the Using Swift category. Note that the Development category is for discussion about developing Swift itself.

(Side note for moderators: is there any way that we can move Using Swift to the top of the category-selection dropdown menu for new posts? I’ve asked about this before and haven’t seen a definitive answer. This type of thing happens a lot with new people posting in the Development section, and I think putting Using Swift at the top of the list would make a noticeable difference.)

In your code example, strs is an array of strings. Each element of that array is a string, but the array itself is, well, an array. Your code does not use any string indexes at all.

It is not clear to me what you are attempting to achieve here. Could you describe in words what you want the function to do?

I could make a guess based on its name, but I think it will be more helpful if you could explain what the input and output of the function are meant to be, and how they are related.

4 Likes

Thank you so much for your reply.
The input ["flower","flow","flight"]
The output ["fl"]

Thank you for the example.

Could you please explain in words how the output is related to the input?

And, in addition to that, could also you please explain in words the step-by-step algorithm that you want to use to produce the output? (Or if you don’t have a specific algorithm in mind, you can say that too.)

This is LeetCode 14.

import Algorithms

func longestCommonPrefix(_ strings: [String]) -> String {
  // It doesn't matter what string we choose.
  // `longestCommonPrefixCount` can't be longer than any of them,
  // and every string contains the longest common prefix.
  let randomString = strings.randomElement()!

  let longestCommonPrefixCount = strings.adjacentPairs()
    .reduce(randomString.count) { count, pair in
      zip(pair.0, pair.1)
        .prefix(count) // ensures that `count` can only stay the same or decrease.
        .prefix(while: ==).count
    }
  return .init(randomString.prefix(longestCommonPrefixCount))
}

or, maybe like

func longestCommonPrefix(_ strings: [String]) -> String {
  .init(
    strings.reduce(.init(strings.randomElement()!)) { commonString, string in
      var iterator = string.makeIterator()
      return commonString.prefix { $0 == iterator.next() }
    }
  )
}
3 Likes

I was attempting to guide the OP through the process of clearly stating their objectives, and thinking through exactly what they want their code to do, rather than simply handing them a solution.

Although now that we’re here I will note that, denoting the lengths of the array as n, the longest string as s, and the longest common prefix as k, your algorithms run in O(ns) time and use O(s) additional space.

The theoretical minimum is O(nk) time and O(k) space. (We must check at least the common prefix of each string, which is n*k, and we must construct the return value which is size k.)

To see why this matters, imagine that all the strings are really long, and all but one are identical except for the last character. The final string is different from near the start. For example, suppose s ≈ n, but k ≈ 1. Then your algorithms would run in O(n²) time and use O(n) space, whereas the best possible would run in O(n) time and use O(1) space.

Now, I can see how to achieve the optimal time and space complexity if we only care about finding a common prefix of, say, the UTF8 code units. But if we want to find a common prefix of the strings themselves in terms of characters, aka extended grapheme clusters, using the standard equivalence rules in Swift, then I don’t see an obvious way to achieve both O(nk) time and O(k) space simultaneously.

Can anyone else?

5 Likes

Thank you so much @Nevin.
I actually have a solution for it, but I was hoping to better understand what each line does—especially how String indices work—so that I can build a deeper understanding and apply it to similar problems in the future.

Your explanation was really helpful and much appreciated!

Thank you @Danny .
I didn't know that .randomElement() can be used here.

1 Like

Unless you have a wrapper, it is unfortunately not possible to index into instances of String by using integers.

Example String Wrapper
String Wrapper
//  StringWrapper.swift

struct StringWrapper {
    let value: String
    private var indexStore: [String.Index]
    
    init (_ value: String) {
        self.value = value
        self.indexStore = []
        var i = self.value.startIndex
        while i != self.value.endIndex {
            self.indexStore.append (i)
            i = self.value.index (after: i)
        }
    }
}

extension StringWrapper {
    subscript (index: Int) -> Character {
        assert (index >= 0)
        assert (index < indexStore.count)
        return value [indexStore [index]]
    }
}

extension StringWrapper: CustomStringConvertible {
    var description: String {
        return value
    }
}
Driver
//  Driver.swift

@main
enum Driver {
    static func main () async {
        do {
            let s = "A 🇦🇫 to Z 🇿🇼 flags!"
            let w = StringWrapper (s)
            print (w.value.count, "characters:", w)
            for i in 0..<w.value.count {
                let c = w [i]
                let us = c.unicodeScalars
                print ("\t", i, c, us, terminator: " ")
                print (us.first!, String (us.first!.value, radix:16).uppercased(), terminator: " ")
                print (us.last!,  String (us.last!.value,  radix:16).uppercased())
            }
        }
    }
}