forked from javadev/LeetCode-in-Kotlin
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
70 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
193 changes: 69 additions & 124 deletions
193
src/main/kotlin/g3201_3300/s3213_construct_string_with_minimum_cost/Solution.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,155 +1,100 @@ | ||
package g3201_3300.s3213_construct_string_with_minimum_cost | ||
|
||
// #Hard #Array #String #Dynamic_Programming #Suffix_Array | ||
// #2024_07_15_Time_3201_ms_(6.67%)_Space_114.1_MB_(6.67%) | ||
// #2024_07_15_Time_1176_ms_(46.67%)_Space_78.1_MB_(33.33%) | ||
|
||
import java.util.Collections | ||
import kotlin.collections.ArrayList | ||
import kotlin.collections.HashMap | ||
import kotlin.math.min | ||
|
||
@Suppress("NAME_SHADOWING") | ||
class Solution { | ||
private fun buildKmpPrefix(target: String): List<Int> { | ||
val w: MutableList<Int> = ArrayList(Collections.nCopies(target.length, 0)) | ||
var k = 0 | ||
var i = 1 | ||
while (i < target.length) { | ||
if (target[i] == target[k]) { | ||
k++ | ||
w[i] = k | ||
i++ | ||
} else { | ||
if (k != 0) { | ||
k = w[k - 1] | ||
} else { | ||
i++ | ||
} | ||
} | ||
private class ACAutomaton { | ||
class Node { | ||
var key: Char = 0.toChar() | ||
var `val`: Int? = null | ||
var len: Int = 0 | ||
val next: Array<Node?> = arrayOfNulls(26) | ||
var suffix: Node? = null | ||
var output: Node? = null | ||
var parent: Node? = null | ||
} | ||
return w | ||
} | ||
|
||
fun find(prefix: List<Int>, target: String, w: String): List<List<Int>> { | ||
val result: MutableList<List<Int>> = ArrayList() | ||
val m = target.length | ||
val n = w.length | ||
var i = 0 | ||
var k = 0 | ||
while (i < m) { | ||
if (target[i] == w[k]) { | ||
i++ | ||
k++ | ||
fun build(patterns: Array<String>, values: IntArray): Node { | ||
val root = Node() | ||
root.suffix = root | ||
root.output = root | ||
for (i in patterns.indices) { | ||
put(root, patterns[i], values[i]) | ||
} | ||
if (k == n) { | ||
result.add(listOf(i - k, i)) | ||
k = prefix[k - 1] | ||
} else if (i < m && target[i] != w[k]) { | ||
if (k != 0) { | ||
k = prefix[k - 1] | ||
for (i in root.next.indices) { | ||
if (root.next[i] == null) { | ||
root.next[i] = root | ||
} else { | ||
i++ | ||
root.next[i]!!.suffix = root | ||
} | ||
} | ||
return root | ||
} | ||
return result | ||
} | ||
|
||
fun minimumCost(target: String, words: Array<String>, costs: IntArray): Int { | ||
val targetPrefix = buildKmpPrefix(target) | ||
val root = Node() | ||
for (j in words.indices) { | ||
val x = words[j] | ||
if (x.length < 320) { | ||
var p: Node? = root | ||
for (i in 0 until x.length) { | ||
val c = x[i] | ||
p!!.children.putIfAbsent(c, Node()) | ||
p = p.children[c] | ||
if (i == x.length - 1) { | ||
if (p!!.cost == null) { | ||
p.cost = costs[j] | ||
} else { | ||
p.cost = min(costs[j], p.cost!!) | ||
} | ||
} | ||
private fun put(root: Node, s: String, `val`: Int) { | ||
var node: Node? = root | ||
for (c in s.toCharArray()) { | ||
if (node!!.next[c.code - 'a'.code] == null) { | ||
node.next[c.code - 'a'.code] = Node() | ||
node.next[c.code - 'a'.code]!!.parent = node | ||
node.next[c.code - 'a'.code]!!.key = c | ||
} | ||
node = node.next[c.code - 'a'.code] | ||
} | ||
} | ||
val dm = | ||
getIntegerMapMap(target, words, costs, targetPrefix) | ||
var d: MutableList<NodeCostPair> = ArrayList() | ||
d.add(NodeCostPair(root, 0)) | ||
val dp = IntArray(target.length + 1) | ||
dp.fill(-1) | ||
dp[0] = 0 | ||
for (i in target.indices) { | ||
val x = target[i] | ||
val q: MutableList<NodeCostPair> = ArrayList() | ||
var t: Int? = null | ||
for (pair in d) { | ||
val p = pair.node | ||
val cost = pair.cost | ||
if (p!!.children.containsKey(x)) { | ||
val w = p.children[x] | ||
if (w!!.cost != null) { | ||
t = if (t == null) cost + w.cost!! else min(t, (cost + w.cost!!)) | ||
} | ||
q.add(NodeCostPair(w, cost)) | ||
} | ||
if (node!!.`val` == null || node.`val`!! > `val`) { | ||
node.`val` = `val` | ||
node.len = s.length | ||
} | ||
t = getInteger(dm, i, dp, t) | ||
if (t != null) { | ||
dp[i + 1] = t | ||
q.add(NodeCostPair(root, t)) | ||
} | ||
|
||
fun getOutput(node: Node?): Node? { | ||
if (node!!.output == null) { | ||
val suffix = getSuffix(node) | ||
node.output = if (suffix!!.`val` != null) suffix else getOutput(suffix) | ||
} | ||
d = q | ||
return node.output | ||
} | ||
return dp[target.length] | ||
} | ||
|
||
private fun getInteger(dm: Map<Int, MutableMap<Int, Int>>, i: Int, dp: IntArray, t: Int?): Int? { | ||
var t = t | ||
val qm = dm.getOrDefault(i + 1, emptyMap()) | ||
for ((b, value) in qm) { | ||
if (dp[b] >= 0) { | ||
t = if (t == null) dp[b] + value else min(t, (dp[b] + value)) | ||
fun go(node: Node?, c: Char): Node? { | ||
if (node!!.next[c.code - 'a'.code] == null) { | ||
node.next[c.code - 'a'.code] = go(getSuffix(node), c) | ||
} | ||
return node.next[c.code - 'a'.code] | ||
} | ||
return t | ||
} | ||
|
||
private fun getIntegerMapMap( | ||
target: String, | ||
words: Array<String>, | ||
costs: IntArray, | ||
targetPrefix: List<Int> | ||
): Map<Int, MutableMap<Int, Int>> { | ||
val dm: MutableMap<Int, MutableMap<Int, Int>> = HashMap() | ||
for (i in words.indices) { | ||
val word = words[i] | ||
if (word.length >= 320) { | ||
val q = find(targetPrefix, target, word) | ||
for (pair in q) { | ||
val b = pair[0] | ||
val e = pair[1] | ||
dm.putIfAbsent(e, HashMap()) | ||
val qm = dm[e]!! | ||
if (qm.containsKey(b)) { | ||
qm[b] = min(qm[b]!!, costs[i]) | ||
} else { | ||
qm[b] = costs[i] | ||
} | ||
private fun getSuffix(node: Node?): Node? { | ||
if (node!!.suffix == null) { | ||
node.suffix = go(getSuffix(node.parent), node.key) | ||
if (node.suffix!!.`val` != null) { | ||
node.output = node.suffix | ||
} else { | ||
node.output = node.suffix!!.output | ||
} | ||
} | ||
return node.suffix | ||
} | ||
return dm | ||
} | ||
|
||
private class Node { | ||
var children: MutableMap<Char, Node> = HashMap() | ||
var cost: Int? = null | ||
fun minimumCost(target: String, words: Array<String>, costs: IntArray): Int { | ||
val ac = ACAutomaton() | ||
val root = ac.build(words, costs) | ||
val dp = IntArray(target.length + 1) | ||
dp.fill(Int.MAX_VALUE / 2) | ||
dp[0] = 0 | ||
var node: ACAutomaton.Node? = root | ||
for (i in 1 until dp.size) { | ||
node = ac.go(node, target[i - 1]) | ||
var temp = node | ||
while (temp != null && temp !== root) { | ||
if (temp.`val` != null && dp[i - temp.len] < Int.MAX_VALUE / 2) { | ||
dp[i] = min(dp[i], (dp[i - temp.len] + temp.`val`!!)) | ||
} | ||
temp = ac.getOutput(temp) | ||
} | ||
} | ||
return if (dp[dp.size - 1] >= Int.MAX_VALUE / 2) -1 else dp[dp.size - 1] | ||
} | ||
|
||
private class NodeCostPair(var node: Node?, var cost: Int) | ||
} |