String
has already a commonPrefix(with:)
method (if Foundation is imported), so one simple solution would be to "fold" that over the entire array of strings:
import Foundation
func longestCommonPrefix(of strings: [String]) -> String {
guard let first = strings.first else { return "" }
return strings.dropFirst().reduce(first) { $0.commonPrefix(with: $1) }
}
A more efficient solution, based on the idea in Find the longest common starting substring in a set of strings, is to
- Find the smallest and largest string in the array.
- Determine the common prefix of just those two strings.
func longestCommonPrefix(of strings: [String]) -> String {
guard let first = strings.first else { return "" }
var (minString, maxString) = (first, first)
for str in strings.dropFirst() {
if str < minString { minString = str }
else if str > maxString { maxString = str }
}
return minString.commonPrefix(with: maxString)
}
Remarks:
- An empty input array could be treated as a fatal error.
- One could use
min()
and max()
instead of the loop, but that
would require two traversals of the string instead of one.
The longest common suffix can then be computed as the (reversed)
longest common prefix of the reversed strings:
func longestCommonSuffix(of strings: [String]) -> String {
let revStrings = strings.map { String($0.reversed()) }
let revPrefix = longestCommonPrefix(of: revStrings)
return String(revPrefix.reversed())
}
Examples:
print(longestCommonPrefix(of: ["A12[1]", "A13[1]", "A14[1]"])) // "A1"
print(longestCommonSuffix(of: ["A12[1]", "A13[1]", "A14[1]"])) // "[1]"
print(longestCommonPrefix(of: ["9-b", "10-b", "11-b"])) // ""
print(longestCommonSuffix(of: ["9-b", "10-b", "11-b"])) // "-b"