|
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