|
1 | 1 | package g3201_3300.s3213_construct_string_with_minimum_cost
|
2 | 2 |
|
3 | 3 | // #Hard #Array #String #Dynamic_Programming #Suffix_Array
|
4 |
| -// #2024_07_09_Time_182_ms_(100.00%)_Space_61.4_MB_(72.97%) |
| 4 | +// #2024_07_15_Time_3201_ms_(6.67%)_Space_114.1_MB_(6.67%) |
5 | 5 |
|
| 6 | +import java.util.Collections |
| 7 | +import kotlin.collections.ArrayList |
| 8 | +import kotlin.collections.HashMap |
6 | 9 | import kotlin.math.min
|
7 | 10 |
|
| 11 | +@Suppress("NAME_SHADOWING") |
8 | 12 | class Solution {
|
9 |
| - private class Node { |
10 |
| - var cost: Int = -1 |
11 |
| - var chd: Array<Node?> = arrayOfNulls(26) |
| 13 | + private fun buildKmpPrefix(target: String): List<Int> { |
| 14 | + val w: MutableList<Int> = ArrayList(Collections.nCopies(target.length, 0)) |
| 15 | + var k = 0 |
| 16 | + var i = 1 |
| 17 | + while (i < target.length) { |
| 18 | + if (target[i] == target[k]) { |
| 19 | + k++ |
| 20 | + w[i] = k |
| 21 | + i++ |
| 22 | + } else { |
| 23 | + if (k != 0) { |
| 24 | + k = w[k - 1] |
| 25 | + } else { |
| 26 | + i++ |
| 27 | + } |
| 28 | + } |
| 29 | + } |
| 30 | + return w |
12 | 31 | }
|
13 | 32 |
|
14 |
| - private var rt: Node? = null |
| 33 | + fun find(prefix: List<Int>, target: String, w: String): List<List<Int>> { |
| 34 | + val result: MutableList<List<Int>> = ArrayList() |
| 35 | + val m = target.length |
| 36 | + val n = w.length |
| 37 | + var i = 0 |
| 38 | + var k = 0 |
| 39 | + while (i < m) { |
| 40 | + if (target[i] == w[k]) { |
| 41 | + i++ |
| 42 | + k++ |
| 43 | + } |
| 44 | + if (k == n) { |
| 45 | + result.add(listOf(i - k, i)) |
| 46 | + k = prefix[k - 1] |
| 47 | + } else if (i < m && target[i] != w[k]) { |
| 48 | + if (k != 0) { |
| 49 | + k = prefix[k - 1] |
| 50 | + } else { |
| 51 | + i++ |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | + return result |
| 56 | + } |
15 | 57 |
|
16 | 58 | fun minimumCost(target: String, words: Array<String>, costs: IntArray): Int {
|
17 |
| - rt = Node() |
18 |
| - val m = words.size |
19 |
| - val n = target.length |
20 |
| - for (i in 0 until m) { |
21 |
| - if (words[i].length <= n) { |
22 |
| - insert(words[i], costs[i]) |
| 59 | + val targetPrefix = buildKmpPrefix(target) |
| 60 | + val root = Node() |
| 61 | + for (j in words.indices) { |
| 62 | + val x = words[j] |
| 63 | + if (x.length < 320) { |
| 64 | + var p: Node? = root |
| 65 | + for (i in 0 until x.length) { |
| 66 | + val c = x[i] |
| 67 | + p!!.children.putIfAbsent(c, Node()) |
| 68 | + p = p.children[c] |
| 69 | + if (i == x.length - 1) { |
| 70 | + if (p!!.cost == null) { |
| 71 | + p.cost = costs[j] |
| 72 | + } else { |
| 73 | + p.cost = min(costs[j], p.cost!!) |
| 74 | + } |
| 75 | + } |
| 76 | + } |
23 | 77 | }
|
24 | 78 | }
|
25 |
| - val dp = IntArray(n + 1) |
26 |
| - dp.fill(INVALID) |
| 79 | + val dm = |
| 80 | + getIntegerMapMap(target, words, costs, targetPrefix) |
| 81 | + var d: MutableList<NodeCostPair> = ArrayList() |
| 82 | + d.add(NodeCostPair(root, 0)) |
| 83 | + val dp = IntArray(target.length + 1) |
| 84 | + dp.fill(-1) |
27 | 85 | dp[0] = 0
|
28 |
| - for (i in 0 until n) { |
29 |
| - if (dp[i] == INVALID) { |
30 |
| - continue |
31 |
| - } |
32 |
| - val nowC = dp[i] |
33 |
| - var now = rt |
34 |
| - var j = i |
35 |
| - while (now != null && j < n) { |
36 |
| - val ch = target[j].code - 'a'.code |
37 |
| - now = now.chd[ch] |
38 |
| - if (now != null && now.cost != -1) { |
39 |
| - dp[j + 1] = min(dp[j + 1].toDouble(), (nowC + now.cost).toDouble()).toInt() |
| 86 | + for (i in target.indices) { |
| 87 | + val x = target[i] |
| 88 | + val q: MutableList<NodeCostPair> = ArrayList() |
| 89 | + var t: Int? = null |
| 90 | + for (pair in d) { |
| 91 | + val p = pair.node |
| 92 | + val cost = pair.cost |
| 93 | + if (p!!.children.containsKey(x)) { |
| 94 | + val w = p.children[x] |
| 95 | + if (w!!.cost != null) { |
| 96 | + t = if (t == null) cost + w.cost!! else min(t, (cost + w.cost!!)) |
| 97 | + } |
| 98 | + q.add(NodeCostPair(w, cost)) |
40 | 99 | }
|
41 |
| - ++j |
42 | 100 | }
|
| 101 | + t = getInteger(dm, i, dp, t) |
| 102 | + if (t != null) { |
| 103 | + dp[i + 1] = t |
| 104 | + q.add(NodeCostPair(root, t)) |
| 105 | + } |
| 106 | + d = q |
43 | 107 | }
|
44 |
| - |
45 |
| - return if (dp[n] == INVALID) -1 else dp[n] |
| 108 | + return dp[target.length] |
46 | 109 | }
|
47 | 110 |
|
48 |
| - private fun insert(wd: String, cst: Int) { |
49 |
| - val len = wd.length |
50 |
| - var now = rt |
51 |
| - for (i in 0 until len) { |
52 |
| - val ch = wd[i].code - 'a'.code |
53 |
| - if (now!!.chd[ch] == null) { |
54 |
| - now.chd[ch] = Node() |
| 111 | + private fun getInteger(dm: Map<Int, MutableMap<Int, Int>>, i: Int, dp: IntArray, t: Int?): Int? { |
| 112 | + var t = t |
| 113 | + val qm = dm.getOrDefault(i + 1, emptyMap()) |
| 114 | + for ((b, value) in qm) { |
| 115 | + if (dp[b] >= 0) { |
| 116 | + t = if (t == null) dp[b] + value else min(t, (dp[b] + value)) |
55 | 117 | }
|
56 |
| - now = now.chd[ch] |
57 | 118 | }
|
58 |
| - if (now!!.cost == -1 || now.cost > cst) { |
59 |
| - now.cost = cst |
| 119 | + return t |
| 120 | + } |
| 121 | + |
| 122 | + private fun getIntegerMapMap( |
| 123 | + target: String, |
| 124 | + words: Array<String>, |
| 125 | + costs: IntArray, |
| 126 | + targetPrefix: List<Int> |
| 127 | + ): Map<Int, MutableMap<Int, Int>> { |
| 128 | + val dm: MutableMap<Int, MutableMap<Int, Int>> = HashMap() |
| 129 | + for (i in words.indices) { |
| 130 | + val word = words[i] |
| 131 | + if (word.length >= 320) { |
| 132 | + val q = find(targetPrefix, target, word) |
| 133 | + for (pair in q) { |
| 134 | + val b = pair[0] |
| 135 | + val e = pair[1] |
| 136 | + dm.putIfAbsent(e, HashMap()) |
| 137 | + val qm = dm[e]!! |
| 138 | + if (qm.containsKey(b)) { |
| 139 | + qm[b] = min(qm[b]!!, costs[i]) |
| 140 | + } else { |
| 141 | + qm[b] = costs[i] |
| 142 | + } |
| 143 | + } |
| 144 | + } |
60 | 145 | }
|
| 146 | + return dm |
61 | 147 | }
|
62 | 148 |
|
63 |
| - companion object { |
64 |
| - private const val INVALID = Int.MAX_VALUE |
| 149 | + private class Node { |
| 150 | + var children: MutableMap<Char, Node> = HashMap() |
| 151 | + var cost: Int? = null |
65 | 152 | }
|
| 153 | + |
| 154 | + private class NodeCostPair(var node: Node?, var cost: Int) |
66 | 155 | }
|
0 commit comments