mirror of
https://gitlab.com/mfocko/LeetCode.git
synced 2024-11-09 15:59:06 +01:00
problems: add „126. Word Ladder II“
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
66888a4109
commit
d533a1774f
1 changed files with 157 additions and 0 deletions
157
problems/word-ladder-ii.kt
Normal file
157
problems/word-ladder-ii.kt
Normal file
|
@ -0,0 +1,157 @@
|
|||
class Solution {
|
||||
private fun wordDifference(lhs: String, rhs: String): Int = lhs.zip(rhs).count { (l, r) -> l != r }
|
||||
|
||||
private enum class VertexState {
|
||||
Unvisited, Enqueued, Done
|
||||
}
|
||||
|
||||
private data class Vertex<T>(
|
||||
val label: T, val neighbours: MutableSet<Vertex<T>> = mutableSetOf()
|
||||
) {
|
||||
var distance: Int = Int.MAX_VALUE
|
||||
val parents: MutableSet<Vertex<T>> = mutableSetOf()
|
||||
var state: VertexState = VertexState.Unvisited
|
||||
private set
|
||||
|
||||
fun add(neighbour: Vertex<T>) {
|
||||
neighbours.add(neighbour)
|
||||
}
|
||||
|
||||
fun addParent(parent: Vertex<T>) {
|
||||
parents.add(parent)
|
||||
}
|
||||
|
||||
fun transition() {
|
||||
state = when (state) {
|
||||
VertexState.Unvisited -> VertexState.Enqueued
|
||||
VertexState.Enqueued -> VertexState.Done
|
||||
VertexState.Done -> error("Cannot transition further!")
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = label.hashCode()
|
||||
override fun toString(): String = "$label(${neighbours.map { it.label.toString() }})"
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Vertex<*>
|
||||
|
||||
if (label != other.label) return false
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private class Graph<T> {
|
||||
private val vertices: MutableMap<T, Vertex<T>> = mutableMapOf()
|
||||
|
||||
fun addVertex(src: T, dst: T) {
|
||||
val u = vertices.getOrPut(src) { Vertex(src) }
|
||||
val v = vertices.getOrPut(dst) { Vertex(dst) }
|
||||
|
||||
u.add(v)
|
||||
}
|
||||
|
||||
fun addSymmetricVertex(src: T, dst: T) {
|
||||
addVertex(src, dst)
|
||||
addVertex(dst, src)
|
||||
}
|
||||
|
||||
private fun processVertex(queue: ArrayDeque<Vertex<T>>, u: Vertex<T>) {
|
||||
u.neighbours.forEach { v ->
|
||||
if (v.state == VertexState.Unvisited) {
|
||||
queue.addLast(v)
|
||||
|
||||
queue.last().distance = u.distance + 1
|
||||
queue.last().transition()
|
||||
} else if (u.distance + 1 != v.distance) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
v.addParent(u)
|
||||
}
|
||||
|
||||
u.transition()
|
||||
}
|
||||
|
||||
fun bfs(src: T, dst: T) {
|
||||
if (vertices[src] == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val queue = ArrayDeque<Vertex<T>>()
|
||||
queue.addLast(vertices[src]!!)
|
||||
|
||||
queue.last().distance = 0
|
||||
queue.last().transition()
|
||||
|
||||
while (queue.isNotEmpty()) {
|
||||
val u = queue.removeFirst()
|
||||
if (u.label == dst) {
|
||||
break
|
||||
}
|
||||
|
||||
processVertex(queue, u)
|
||||
}
|
||||
}
|
||||
|
||||
fun parents(vertex: T): List<T> = vertices[vertex]?.parents?.map(Vertex<T>::label) ?: emptyList()
|
||||
|
||||
override fun toString(): String = vertices.toString()
|
||||
}
|
||||
|
||||
private fun constructGraph(src: String, vertices: List<String>): Graph<String> {
|
||||
val g = Graph<String>()
|
||||
|
||||
vertices
|
||||
.filter { wordDifference(src, it) == 1 }
|
||||
.forEach { g.addVertex(src, it) }
|
||||
|
||||
vertices.indices
|
||||
.flatMap { listOf(it).zip(it + 1 until vertices.size) }
|
||||
.map { (i, j) -> vertices[i] to vertices[j] }
|
||||
.filter { (lhs, rhs) -> wordDifference(lhs, rhs) == 1 }
|
||||
.forEach { (lhs, rhs) -> g.addSymmetricVertex(lhs, rhs) }
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
private fun constructPath(
|
||||
graph: Graph<String>, currentPath: MutableList<String>, paths: MutableList<MutableList<String>>
|
||||
): MutableList<MutableList<String>> {
|
||||
if (graph.parents(currentPath.last()).isEmpty()) {
|
||||
paths.add(currentPath.reversed().toMutableList())
|
||||
return paths
|
||||
}
|
||||
|
||||
for (parent in graph.parents(currentPath.last()).filter { !currentPath.contains(it) }) {
|
||||
currentPath.add(parent)
|
||||
constructPath(graph, currentPath, paths)
|
||||
currentPath.remove(parent)
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
fun findLadders(
|
||||
beginWord: String, endWord: String, wordList: List<String>
|
||||
): List<List<String>> {
|
||||
val graph = constructGraph(beginWord, wordList)
|
||||
graph.bfs(beginWord, endWord)
|
||||
return constructPath(graph, mutableListOf(endWord), mutableListOf()).filter { it.count() >= 2 }
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val s = Solution()
|
||||
|
||||
check(
|
||||
s.findLadders("hit", "cog", listOf("hot", "dot", "dog", "lot", "log", "cog")) == listOf(
|
||||
listOf("hit", "hot", "dot", "dog", "cog"), listOf("hit", "hot", "lot", "log", "cog")
|
||||
)
|
||||
)
|
||||
check(s.findLadders("hit", "cog", listOf("hot", "dot", "dog", "lot", "log")) == emptyList<List<String>>())
|
||||
check(s.findLadders("hot", "dog", listOf("hot", "dog")) == emptyList<List<String>>())
|
||||
check(s.findLadders("hot", "dog", listOf("hot", "dog", "dot")) == listOf(listOf("hot", "dot", "dog")))
|
||||
}
|
Loading…
Reference in a new issue