diff --git a/build.gradle.kts b/build.gradle.kts index 42aa4594..9adecd08 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ plugins { kotlin("jvm") version "2.0.0" jacoco - id("org.sonarqube") version "5.0.0.4638" + id("org.sonarqube") version "5.1.0.4882" id("com.diffplug.spotless") version "6.12.0" `maven-publish` } 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 add26f2b..272b1b63 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,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 { - 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++ - } - } + private class ACAutomaton { + class Node { + var key: Char = 0.toChar() + var `val`: Int? = null + var len: Int = 0 + val next: Array = arrayOfNulls(26) + var suffix: Node? = null + var output: Node? = null + var parent: Node? = null } - return w - } - 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++ + fun build(patterns: Array, 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, 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 = 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 = 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>, 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, - 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] - } + 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 = HashMap() - var cost: Int? = null + fun minimumCost(target: String, words: Array, 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) }