目标
给你一个字符串数组 words 和一个字符串 target。
如果字符串 x 是 words 中 任意 字符串的 前缀,则认为 x 是一个 有效 字符串。
现计划通过 连接 有效字符串形成 target ,请你计算并返回需要连接的 最少 字符串数量。如果无法通过这种方式形成 target,则返回 -1。
示例 1:
输入: words = ["abc","aaaaa","bcdef"], target = "aabcdabc"
输出: 3
解释:
target 字符串可以通过连接以下有效字符串形成:
words[1] 的长度为 2 的前缀,即 "aa"。
words[2] 的长度为 3 的前缀,即 "bcd"。
words[0] 的长度为 3 的前缀,即 "abc"。
示例 2:
输入: words = ["abababab","ab"], target = "ababaababa"
输出: 2
解释:
target 字符串可以通过连接以下有效字符串形成:
words[0] 的长度为 5 的前缀,即 "ababa"。
words[0] 的长度为 5 的前缀,即 "ababa"。
示例 3:
输入: words = ["abcdef"], target = "xyz"
输出: -1
说明:
- 1 <= words.length <= 100
- 1 <= words[i].length <= 5 * 10^3
- 输入确保 sum(words[i].length) <= 10^5。
- words[i] 只包含小写英文字母。
- 1 <= target.length <= 5 * 10^3
- target 只包含小写英文字母。
思路
有一个字符串数组 words
和目标字符串 target
,请你使用最少的字符串前缀组成 target
,返回需要的字符串数量,如果无法组成 target
返回 -1
。
注意前缀是允许重复使用的,状态个数为 target.length ^ 2
,深度为 100
,直接使用记忆化搜索会超时。
使用字典树加动态规划可以勉强通过,但是明天的通过不了。
//todo KMP 算法 Z 函数 / 字符串哈希+二分 / AC 自动机
代码
/**
* @date 2024-12-17 8:59
*/
public class MinValidStrings3291 {
static public class Trie {
public boolean isLeaf;
public Trie[] children;
public Trie() {
this.children = new Trie[26];
}
public Trie build(String[] dict) {
Trie root = this;
for (String word : dict) {
root = this;
char[] chars = word.toCharArray();
for (int i = 0; i < chars.length; i++) {
int c = chars[i] - 'a';
if (root.children[c] == null) {
root.children[c] = new Trie();
}
root = root.children[c];
}
root.isLeaf = true;
}
return root;
}
public int exists(char[] target, int start) {
int n = target.length;
int length = 0;
Trie root = this;
int c = target[start] - 'a';
while (root.children[c] != null) {
root = root.children[c];
length++;
start++;
if (start == n) {
break;
}
c = target[start] - 'a';
}
return length;
}
}
public int minValidStrings_v1(String[] words, String target) {
Trie root = new Trie();
root.build(words);
char[] chars = target.toCharArray();
int n = chars.length;
int[] dp = new int[n];
Arrays.fill(dp, Integer.MAX_VALUE);
int length = root.exists(chars, 0);
for (int i = 0; i < length; i++) {
dp[i] = 1;
}
for (int i = 1; i < n; i++) {
if (dp[i - 1] == Integer.MAX_VALUE) {
return -1;
}
length = root.exists(chars, i);
for (int j = i; j < i + length; j++) {
dp[j] = Math.min(dp[j], dp[i - 1] + 1);
}
}
return dp[n - 1] == Integer.MAX_VALUE ? -1 : dp[n - 1];
}
}