diff --git a/src/main/kotlin/g3201_3300/s3213_construct_string_with_minimum_cost/Solution.kt b/src/main/kotlin/g3201_3300/s3213_construct_string_with_minimum_cost/Solution.kt index 727feeb8..add26f2b 100644 --- a/src/main/kotlin/g3201_3300/s3213_construct_string_with_minimum_cost/Solution.kt +++ b/src/main/kotlin/g3201_3300/s3213_construct_string_with_minimum_cost/Solution.kt @@ -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 = arrayOfNulls(26) + private fun buildKmpPrefix(target: String): List { + val w: MutableList = 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, target: String, w: String): List> { + val result: MutableList> = 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, 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 = 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 = 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>, 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, + costs: IntArray, + targetPrefix: List + ): Map> { + val dm: MutableMap> = 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 = HashMap() + var cost: Int? = null } + + private class NodeCostPair(var node: Node?, var cost: Int) }