Here's an alternative solution that uses a bitmask:
fun String.masterCompare(otherString: String): Pair<Int, Int>? =
when {
length != otherString.length -> null
else -> {
// this part is really easy
val commonAtSameIndex = zip(otherString).count {
(one, another) -> one == another
}
val countedAlready = BooleanArray(length)
val commonOverall = map {
character -> otherString
.withIndex()
.filterNot {
(index, _) -> countedAlready[index]
}
.firstOrNull {
(_, value) -> character == value
}
?.index
?.also {
countedAlready[it] = true
}
}
.count { it != null }
commonOverall to commonAtSameIndex
}
}
And finally a version that combines the functional way - in my opinion much clearer - of calculating the number of identical characters in the same positions with the loop that calculates the other value.
fun String.masterCompare(otherString: String): Pair<Int, Int>? =
when {
length != otherString.length -> null
else -> {
// this part is really easy
val commonAtSameIndex = zip(otherString).count {
(one, another) -> one == another
}
var commonOverall: Int = 0
val countedAlready = BooleanArray(length)
/* this is yet another detail: there's no need for toList() allocation.
* you can iterate over this (String) straight away. */
for (c in this) {
/* find the first occurrence of the c character in otherString
* that wasn't counted already */
val index = otherStringcountedAlready
.asSequence()
.withIndex()
.filterfilterNot {
countedAlready[it.index].not() && it.value == c
}
.mapindexOfFirst {
itotherString[it.index
index] == c }
.firstOrNull()
if (index !=>= null0) {
commonOverall++
countedAlready[index] = true
countedAlready[index] = truecommonOverall++
}
}
commonOverall to commonAtSameIndex
}
}