|
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_15_Time_3201_ms_(6.67%)_Space_114.1_MB_(6.67%) |
| 4 | +// #2024_07_15_Time_1176_ms_(46.67%)_Space_78.1_MB_(33.33%) |
5 | 5 |
|
6 | | -import java.util.Collections |
7 | | -import kotlin.collections.ArrayList |
8 | | -import kotlin.collections.HashMap |
9 | 6 | import kotlin.math.min |
10 | 7 |
|
11 | | -@Suppress("NAME_SHADOWING") |
12 | 8 | class Solution { |
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 | | - } |
| 9 | + private class ACAutomaton { |
| 10 | + class Node { |
| 11 | + var key: Char = 0.toChar() |
| 12 | + var `val`: Int? = null |
| 13 | + var len: Int = 0 |
| 14 | + val next: Array<Node?> = arrayOfNulls(26) |
| 15 | + var suffix: Node? = null |
| 16 | + var output: Node? = null |
| 17 | + var parent: Node? = null |
29 | 18 | } |
30 | | - return w |
31 | | - } |
32 | 19 |
|
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++ |
| 20 | + fun build(patterns: Array<String>, values: IntArray): Node { |
| 21 | + val root = Node() |
| 22 | + root.suffix = root |
| 23 | + root.output = root |
| 24 | + for (i in patterns.indices) { |
| 25 | + put(root, patterns[i], values[i]) |
43 | 26 | } |
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] |
| 27 | + for (i in root.next.indices) { |
| 28 | + if (root.next[i] == null) { |
| 29 | + root.next[i] = root |
50 | 30 | } else { |
51 | | - i++ |
| 31 | + root.next[i]!!.suffix = root |
52 | 32 | } |
53 | 33 | } |
| 34 | + return root |
54 | 35 | } |
55 | | - return result |
56 | | - } |
57 | 36 |
|
58 | | - fun minimumCost(target: String, words: Array<String>, costs: IntArray): Int { |
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 | | - } |
| 37 | + private fun put(root: Node, s: String, `val`: Int) { |
| 38 | + var node: Node? = root |
| 39 | + for (c in s.toCharArray()) { |
| 40 | + if (node!!.next[c.code - 'a'.code] == null) { |
| 41 | + node.next[c.code - 'a'.code] = Node() |
| 42 | + node.next[c.code - 'a'.code]!!.parent = node |
| 43 | + node.next[c.code - 'a'.code]!!.key = c |
76 | 44 | } |
| 45 | + node = node.next[c.code - 'a'.code] |
77 | 46 | } |
78 | | - } |
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) |
85 | | - dp[0] = 0 |
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)) |
99 | | - } |
| 47 | + if (node!!.`val` == null || node.`val`!! > `val`) { |
| 48 | + node.`val` = `val` |
| 49 | + node.len = s.length |
100 | 50 | } |
101 | | - t = getInteger(dm, i, dp, t) |
102 | | - if (t != null) { |
103 | | - dp[i + 1] = t |
104 | | - q.add(NodeCostPair(root, t)) |
| 51 | + } |
| 52 | + |
| 53 | + fun getOutput(node: Node?): Node? { |
| 54 | + if (node!!.output == null) { |
| 55 | + val suffix = getSuffix(node) |
| 56 | + node.output = if (suffix!!.`val` != null) suffix else getOutput(suffix) |
105 | 57 | } |
106 | | - d = q |
| 58 | + return node.output |
107 | 59 | } |
108 | | - return dp[target.length] |
109 | | - } |
110 | 60 |
|
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)) |
| 61 | + fun go(node: Node?, c: Char): Node? { |
| 62 | + if (node!!.next[c.code - 'a'.code] == null) { |
| 63 | + node.next[c.code - 'a'.code] = go(getSuffix(node), c) |
117 | 64 | } |
| 65 | + return node.next[c.code - 'a'.code] |
118 | 66 | } |
119 | | - return t |
120 | | - } |
121 | 67 |
|
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 | | - } |
| 68 | + private fun getSuffix(node: Node?): Node? { |
| 69 | + if (node!!.suffix == null) { |
| 70 | + node.suffix = go(getSuffix(node.parent), node.key) |
| 71 | + if (node.suffix!!.`val` != null) { |
| 72 | + node.output = node.suffix |
| 73 | + } else { |
| 74 | + node.output = node.suffix!!.output |
143 | 75 | } |
144 | 76 | } |
| 77 | + return node.suffix |
145 | 78 | } |
146 | | - return dm |
147 | 79 | } |
148 | 80 |
|
149 | | - private class Node { |
150 | | - var children: MutableMap<Char, Node> = HashMap() |
151 | | - var cost: Int? = null |
| 81 | + fun minimumCost(target: String, words: Array<String>, costs: IntArray): Int { |
| 82 | + val ac = ACAutomaton() |
| 83 | + val root = ac.build(words, costs) |
| 84 | + val dp = IntArray(target.length + 1) |
| 85 | + dp.fill(Int.MAX_VALUE / 2) |
| 86 | + dp[0] = 0 |
| 87 | + var node: ACAutomaton.Node? = root |
| 88 | + for (i in 1 until dp.size) { |
| 89 | + node = ac.go(node, target[i - 1]) |
| 90 | + var temp = node |
| 91 | + while (temp != null && temp !== root) { |
| 92 | + if (temp.`val` != null && dp[i - temp.len] < Int.MAX_VALUE / 2) { |
| 93 | + dp[i] = min(dp[i], (dp[i - temp.len] + temp.`val`!!)) |
| 94 | + } |
| 95 | + temp = ac.getOutput(temp) |
| 96 | + } |
| 97 | + } |
| 98 | + return if (dp[dp.size - 1] >= Int.MAX_VALUE / 2) -1 else dp[dp.size - 1] |
152 | 99 | } |
153 | | - |
154 | | - private class NodeCostPair(var node: Node?, var cost: Int) |
155 | 100 | } |
0 commit comments