Hard
You are given a string target, an array of strings words, and an integer array costs, both arrays of the same length.
Imagine an empty string s.
You can perform the following operation any number of times (including zero):
i in the range [0, words.length - 1].words[i] to s.costs[i].Return the minimum cost to make s equal to target. If it’s not possible, return -1.
Example 1:
Input: target = “abcdef”, words = [“abdef”,”abc”,”d”,”def”,”ef”], costs = [100,1,1,10,5]
Output: 7
Explanation:
The minimum cost can be achieved by performing the following operations:
"abc" to s at a cost of 1, resulting in s = "abc"."d" to s at a cost of 1, resulting in s = "abcd"."ef" to s at a cost of 5, resulting in s = "abcdef".Example 2:
Input: target = “aaaa”, words = [“z”,”zz”,”zzz”], costs = [1,10,100]
Output: -1
Explanation:
It is impossible to make s equal to target, so we return -1.
Constraints:
1 <= target.length <= 5 * 1041 <= words.length == costs.length <= 5 * 1041 <= words[i].length <= target.lengthwords[i].length is less than or equal to 5 * 104.target and words[i] consist only of lowercase English letters.1 <= costs[i] <= 104import kotlin.math.min
class Solution {
private class ACAutomaton {
class Node {
var key: Char = 0.toChar()
var `val`: Int? = null
var len: Int = 0
val next: Array<Node?> = arrayOfNulls(26)
var suffix: Node? = null
var output: Node? = null
var parent: Node? = null
}
fun build(patterns: Array<String>, values: IntArray): Node {
val root = Node()
root.suffix = root
root.output = root
for (i in patterns.indices) {
put(root, patterns[i], values[i])
}
for (i in root.next.indices) {
if (root.next[i] == null) {
root.next[i] = root
} else {
root.next[i]!!.suffix = root
}
}
return root
}
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]
}
if (node!!.`val` == null || node.`val`!! > `val`) {
node.`val` = `val`
node.len = s.length
}
}
fun getOutput(node: Node?): Node? {
if (node!!.output == null) {
val suffix = getSuffix(node)
node.output = if (suffix!!.`val` != null) suffix else getOutput(suffix)
}
return node.output
}
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]
}
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
}
}
fun minimumCost(target: String, words: Array<String>, 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]
}
}