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 java.util.Arrays;
public class Solution {
private static class ACAutomaton {
private static class Node {
private char key;
private Integer val = null;
private int len;
private final Node[] next = new Node[26];
private Node suffix = null;
private Node output = null;
private Node parent = null;
}
public Node build(String[] patterns, int[] values) {
Node root = new Node();
root.suffix = root;
root.output = root;
for (int i = 0; i < patterns.length; i++) {
put(root, patterns[i], values[i]);
}
for (int i = 0; i < root.next.length; i++) {
if (root.next[i] == null) {
root.next[i] = root;
} else {
root.next[i].suffix = root;
}
}
return root;
}
private void put(Node root, String s, int val) {
Node node = root;
for (char c : s.toCharArray()) {
if (node.next[c - 'a'] == null) {
node.next[c - 'a'] = new Node();
node.next[c - 'a'].parent = node;
node.next[c - 'a'].key = c;
}
node = node.next[c - 'a'];
}
if (node.val == null || node.val > val) {
node.val = val;
node.len = s.length();
}
}
public Node getOutput(Node node) {
if (node.output == null) {
Node suffix = getSuffix(node);
node.output = suffix.val != null ? suffix : getOutput(suffix);
}
return node.output;
}
private Node go(Node node, char c) {
if (node.next[c - 'a'] == null) {
node.next[c - 'a'] = go(getSuffix(node), c);
}
return node.next[c - 'a'];
}
private Node getSuffix(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;
}
}
public int minimumCost(String target, String[] words, int[] costs) {
ACAutomaton ac = new ACAutomaton();
ACAutomaton.Node root = ac.build(words, costs);
int[] dp = new int[target.length() + 1];
Arrays.fill(dp, Integer.MAX_VALUE / 2);
dp[0] = 0;
ACAutomaton.Node node = root;
for (int i = 1; i < dp.length; i++) {
if (node != null) {
node = ac.go(node, target.charAt(i - 1));
}
for (ACAutomaton.Node temp = node;
temp != null && temp != root;
temp = ac.getOutput(temp)) {
if (temp.val != null && dp[i - temp.len] < Integer.MAX_VALUE / 2) {
dp[i] = Math.min(dp[i], dp[i - temp.len] + temp.val);
}
}
}
return dp[dp.length - 1] >= Integer.MAX_VALUE / 2 ? -1 : dp[dp.length - 1];
}
}