2025-04-23:形成目标字符串需要的最少字符串数Ⅱ。用go语言,给定一个字符串数组 words 和一个目标字符串 targ
2025-04-23:形成目标字符串需要的最少字符串数Ⅱ。用go语言,给定一个字符串数组 words 和一个目标字符串 target。
如果某个字符串 x 是数组 words 中任意字符串的前缀,则称 x 是一个有效字符串。
现在希望通过拼接若干个有效字符串,组成目标字符串 target。请计算完成这个拼接所需的最少字符串数量,若无法拼接出 target,则返回 -1。
1 <= words.length <= 100。
1 <= words[i].length <= 5 * 10000。
输入确保 sum(words[i].length) <= 100000。
words[i] 只包含小写英文字母。
1 <= target.length <= 5 * 10000。
target 只包含小写英文字母。
输入: words = [“abc”,“aaaaa”,“bcdef”], target = “aabcdabc”。
输出: 3。
解释:
target 字符串可以通过连接以下有效字符串形成:
words[1] 的长度为 2 的前缀,即 “aa”。
words[2] 的长度为 3 的前缀,即 “bcd”。
words[0] 的长度为 3 的前缀,即 “abc”。
题目来自leetcode3292。
解决思路
-
预处理阶段:
- 对于目标字符串
target的每一个位置i,我们需要知道从words中所有字符串的前缀中,能够覆盖target的前i个字符的最长前缀长度。这可以通过 KMP 算法的前缀函数(prefix function)来实现。 - 具体来说,对于
words中的每一个字符串word,我们计算word + "#" + target的前缀函数数组pi。这样,pi数组的后半部分(即target的部分)会告诉我们word的前缀与target的各个子串的最长匹配长度。 - 对于
target的每一个位置i,我们记录所有words中字符串的前缀能够覆盖target前i个字符的最长长度back[i]。
- 对于目标字符串
-
动态规划阶段:
- 我们使用动态规划数组
dp,其中dp[i]表示构造target的前i个字符所需的最少有效字符串数量。 - 初始化时,
dp[0] = 0(空字符串不需要任何有效字符串),其余dp[i]初始化为一个很大的值(表示不可达)。 - 对于
target的每一个位置i,我们利用预处理阶段得到的back[i]来更新dp[i + 1]。具体来说,dp[i + 1] = min(dp[i + 1], dp[i + 1 - back[i]] + 1)。 - 如果在动态规划过程中发现某个
dp[i]的值超过了target的长度,说明无法构造target,直接返回 -1。
- 我们使用动态规划数组
-
结果提取:
- 最终
dp[n]的值就是构造target所需的最少有效字符串数量。如果dp[n]仍然是初始化的很大值,说明无法构造target,返回 -1。
- 最终
具体步骤
-
计算
back数组:- 对于
words中的每一个字符串word,计算word + "#" + target的前缀函数pi。 - 对于
target的每一个位置i,back[i]是所有words中字符串的前缀函数在target部分(即pi[m + 1 + i],其中m是word的长度)的最大值。
- 对于
-
动态规划填充
dp数组:- 初始化
dp[0] = 0,其余dp[i] = ∞。 - 对于
i从0到n - 1:- 如果
back[i] == 0,说明无法从words中找到一个前缀覆盖target的前i + 1个字符,跳过。 - 否则,
dp[i + 1] = min(dp[i + 1], dp[i + 1 - back[i]] + 1)。 - 如果
dp[i + 1] > n,直接返回 -1。
- 如果
- 初始化
-
返回结果:
- 如果
dp[n]仍然是∞,返回 -1;否则返回dp[n]。
- 如果
时间复杂度和空间复杂度
- 时间复杂度:
- 预处理阶段:对于每一个
word,计算word + "#" + target的前缀函数的时间复杂度为O(m + n),其中m是word的长度,n是target的长度。由于sum(words[i].length) <= 100000,预处理阶段的总时间复杂度为O(sum(m) + k * n),其中k是words的数量。由于k <= 100,可以简化为O(sum(m) + n)。 - 动态规划阶段:填充
dp数组的时间复杂度为O(n)。 - 总时间复杂度:
O(sum(m) + n)。
- 预处理阶段:对于每一个
- 空间复杂度:
back数组:O(n)。dp数组:O(n)。- 前缀函数
pi:O(m + n)(临时空间,可以复用)。 - 总空间复杂度:
O(n + m)(其中m是最大的word长度)。
Go完整代码如下:
package main
import (
"fmt"
"math"
)
func minValidStrings(words []string, target string) int {
prefixFunction := func(word, target string) []int {
s := word + "#" + target
n := len(s)
pi := make([]int, n)
for i := 1; i < n; i++ {
j := pi[i-1]
for j > 0 && s[i] != s[j] {
j = pi[j-1]
}
if s[i] == s[j] {
j++
}
pi[i] = j
}
return pi
}
n := len(target)
back := make([]int, n)
for _, word := range words {
pi := prefixFunction(word, target)
m := len(word)
for i := 0; i < n; i++ {
back[i] = int(math.Max(float64(back[i]), float64(pi[m+1+i])))
}
}
dp := make([]int, n+1)
for i := 1; i <= n; i++ {
dp[i] = int(1e9)
}
for i := 0; i < n; i++ {
dp[i+1] = dp[i+1-back[i]] + 1
if dp[i+1] > n {
return -1
}
}
return dp[n]
}
func main() {
words := []string{"abc", "aaaaa", "bcdef"}
target := "aabcdabc"
results := minValidStrings(words, target)
fmt.Println(results)
}

Python完整代码如下:
# -*-coding:utf-8-*-
def minValidStrings(words, target):
n = len(target)
if n == 0:
return 0
def prefix_function(word, target_str):
s = word + '#' + target_str
pi = [0] * len(s)
for i in range(1, len(s)):
j = pi[i-1]
while j > 0 and s[i] != s[j]:
j = pi[j-1]
if s[i] == s[j]:
j += 1
pi[i] = j
return pi
back = [0] * n
for word in words:
m = len(word)
if m == 0:
continue
pi = prefix_function(word, target)
for i in range(n):
pos = m + 1 + i
current_pi = pi[pos] if pos < len(pi) else 0
if current_pi > back[i]:
back[i] = current_pi
dp = [float('inf')] * (n + 1)
dp[0] = 0
for i in range(n):
k = back[i]
prev = (i + 1) - k
if prev < 0:
return -1
if dp[prev] + 1 < dp[i+1]:
dp[i+1] = dp[prev] + 1
if dp[i+1] > n:
return -1
return dp[n] if dp[n] != float('inf') else -1
# 示例测试
if __name__ == "__main__":
words = ["abc", "aaaaa", "bcdef"]
target = "aabcdabc"
print(minValidStrings(words, target))

- 随机文章
- 热门文章
- 热评文章
- 我的成长故事
- 国内最大的MCP中文社区来了,4000多个服务等你体验
- 解密数据库的MPP模式
- 人格瓶子小测试选一个瓶子分析你的人格
- Java 面向对象设计:如何写出高内聚、低耦合的代码?
- 心理测试 测测你给别人的感觉
- 拖拽式低代码引擎架构——企业级系统研发成本压缩90%
- 从操作系统到智能计算:openEuler在深度学习中的应用探索【华为根技术】
- Java JWT 认证系统
回归分析



