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
1 changed file
with
129 additions
and
40 deletions.
There are no files selected for viewing
169 changes: 129 additions & 40 deletions
169
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,66 +1,155 @@ | ||
package g3201_3300.s3213_construct_string_with_minimum_cost | ||
|
||
// #Hard #Array #String #Dynamic_Programming #Suffix_Array | ||
// #2024_07_09_Time_182_ms_(100.00%)_Space_61.4_MB_(72.97%) | ||
// #2024_07_15_Time_3201_ms_(6.67%)_Space_114.1_MB_(6.67%) | ||
|
||
import java.util.Collections | ||
import kotlin.collections.ArrayList | ||
import kotlin.collections.HashMap | ||
import kotlin.math.min | ||
|
||
@Suppress("NAME_SHADOWING") | ||
class Solution { | ||
private class Node { | ||
var cost: Int = -1 | ||
var chd: Array<Node?> = arrayOfNulls(26) | ||
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++ | ||
} | ||
} | ||
} | ||
return w | ||
} | ||
|
||
private var rt: Node? = null | ||
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++ | ||
} | ||
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] | ||
} else { | ||
i++ | ||
} | ||
} | ||
} | ||
return result | ||
} | ||
|
||
fun minimumCost(target: String, words: Array<String>, costs: IntArray): Int { | ||
rt = Node() | ||
val m = words.size | ||
val n = target.length | ||
for (i in 0 until m) { | ||
if (words[i].length <= n) { | ||
insert(words[i], costs[i]) | ||
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!!) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
val dp = IntArray(n + 1) | ||
dp.fill(INVALID) | ||
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 0 until n) { | ||
if (dp[i] == INVALID) { | ||
continue | ||
} | ||
val nowC = dp[i] | ||
var now = rt | ||
var j = i | ||
while (now != null && j < n) { | ||
val ch = target[j].code - 'a'.code | ||
now = now.chd[ch] | ||
if (now != null && now.cost != -1) { | ||
dp[j + 1] = min(dp[j + 1].toDouble(), (nowC + now.cost).toDouble()).toInt() | ||
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)) | ||
} | ||
++j | ||
} | ||
t = getInteger(dm, i, dp, t) | ||
if (t != null) { | ||
dp[i + 1] = t | ||
q.add(NodeCostPair(root, t)) | ||
} | ||
d = q | ||
} | ||
|
||
return if (dp[n] == INVALID) -1 else dp[n] | ||
return dp[target.length] | ||
} | ||
|
||
private fun insert(wd: String, cst: Int) { | ||
val len = wd.length | ||
var now = rt | ||
for (i in 0 until len) { | ||
val ch = wd[i].code - 'a'.code | ||
if (now!!.chd[ch] == null) { | ||
now.chd[ch] = Node() | ||
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)) | ||
} | ||
now = now.chd[ch] | ||
} | ||
if (now!!.cost == -1 || now.cost > cst) { | ||
now.cost = cst | ||
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] | ||
} | ||
} | ||
} | ||
} | ||
return dm | ||
} | ||
|
||
companion object { | ||
private const val INVALID = Int.MAX_VALUE | ||
private class Node { | ||
var children: MutableMap<Char, Node> = HashMap() | ||
var cost: Int? = null | ||
} | ||
|
||
private class NodeCostPair(var node: Node?, var cost: Int) | ||
} |