2025-02-26:最小代价构造字符串。用go语言,给定一个目标字符串 target、一个字符串数组 words 和一个整数数
【摘要】 2025-02-26:最小代价构造字符串。用go语言,给定一个目标字符串 target、一个字符串数组 words 和一个整数数组 costs,这两个数组的长度相同。想象一个空字符串 s,你可以执行以下操作任意次数(包括0次):1.从 words 数组中选择一个索引 i,满足 0 ≤ i < words.length。2.将 words[i] 添加到 s。3.进行此操作的费用为 costs[...
2025-02-26:最小代价构造字符串。用go语言,给定一个目标字符串 target、一个字符串数组 words 和一个整数数组 costs,这两个数组的长度相同。
想象一个空字符串 s,你可以执行以下操作任意次数(包括0次):
1.从 words 数组中选择一个索引 i,满足 0 ≤ i < words.length。
2.将 words[i] 添加到 s。
3.进行此操作的费用为 costs[i]。
你的任务是返回使得 s 等于 target 的最小花费。如果无法构造出 target,则返回 -1。
1 <= target.length <= 5 * 10000。
1 <= words.length == costs.length <= 5 * 10000。
1 <= words[i].length <= target.length。
所有 words[i].length 的总和小于或等于 5 * 10000。
target 和 words[i] 仅由小写英文字母组成。
1 <= costs[i] <= 10000。
输入: target = “abcdef”, words = [“abdef”,“abc”,“d”,“def”,“ef”], costs = [100,1,1,10,5]。
输出: 7。
解释:
选择索引 1 并以成本 1 将 “abc” 追加到 s,得到 s = “abc”。
选择索引 2 并以成本 1 将 “d” 追加到 s,得到 s = “abcd”。
选择索引 4 并以成本 5 将 “ef” 追加到 s,得到 s = “abcdef”。
答案2025-02-26:
题目来自leetcode3213。
大体步骤如下:
1.创建一个 AC 自动机的结构体,其中包含节点的定义和相关方法。
2.创建一个 put 方法,用于将单词插入到 AC 自动机中,同时记录对应的费用。
3.创建一个 buildFail 方法,用于构建 AC 自动机的失配指针和后缀链接。
4.创建一个 minimumCost 函数,接收目标字符串 target、单词数组 words 和对应费用数组 costs。
5.在 minimumCost 函数中,建立 AC 自动机并对其进行构建。
6.初始化一个状态数组 f,用于记录到达每个位置的最小花费。
7.遍历目标字符串 target 中的每个字符,更新状态数组 f 中的值。
8.返回状态数组 f 最后一个位置的值作为最小花费,如果无法构造出 target,则返回 -1。
9.在 main 函数中给定目标字符串 target、单词数组 words 和费用数组 costs,调用 minimumCost 函数并输出结果。
总的时间复杂度取决于构建 AC 自动机和更新状态数组 f 的过程,构建 AC 自动机的时间复杂度为 O(∑len(words)),更新状态数组 f 的时间复杂度为 O(n),其中 n 为目标字符串 target 的长度。因此,总的时间复杂度为 O(∑len(words) + n)。
总的额外空间复杂度主要消耗在构建 AC 自动机时的存储空间和状态数组 f 的空间,构建 AC 自动机的额外空间复杂度为 O(∑len(words)),状态数组 f 的额外空间复杂度为 O(n)。因此,总的额外空间复杂度为 O(∑len(words) + n)。
Go完整代码如下:
package main
import (
"fmt"
"math"
)
type node struct {
son [26]*node
fail *node // 当 cur.son[i] 不能匹配 target 中的某个字符时,cur.fail.son[i] 即为下一个待匹配节点(等于 root 则表示没有匹配)
last *node // 后缀链接(suffix link),用来快速跳到一定是某个 words[k] 的最后一个字母的节点(等于 root 则表示没有)
len int
cost int
}
type acam struct {
root *node
}
func (ac *acam) put(s string, cost int) {
cur := ac.root
for _, b := range s {
b -= 'a'
if cur.son[b] == nil {
cur.son[b] = &node{cost: math.MaxInt}
}
cur = cur.son[b]
}
cur.len = len(s)
cur.cost = min(cur.cost, cost)
}
func (ac *acam) buildFail() {
ac.root.fail = ac.root
ac.root.last = ac.root
q := []*node{}
for i, son := range ac.root.son[:] {
if son == nil {
ac.root.son[i] = ac.root
} else {
son.fail = ac.root // 第一层的失配指针,都指向根节点 ∅
son.last = ac.root
q = append(q, son)
}
}
// BFS
for len(q) > 0 {
cur := q[0]
q = q[1:]
for i, son := range cur.son[:] {
if son == nil {
// 虚拟子节点 cur.son[i],和 cur.fail.son[i] 是同一个
// 方便失配时直接跳到下一个可能匹配的位置(但不一定是某个 words[k] 的最后一个字母)
cur.son[i] = cur.fail.son[i]
continue
}
son.fail = cur.fail.son[i] // 计算失配位置
if son.fail.len > 0 {
son.last = son.fail
} else {
// 沿着 last 往上走,可以直接跳到一定是某个 words[k] 的最后一个字母的节点(如果跳到 root 表示没有匹配)
son.last = son.fail.last
}
q = append(q, son)
}
}
}
func minimumCost(target string, words []string, costs []int) int {
ac := &acam{root: &node{}}
for i, w := range words {
ac.put(w, costs[i])
}
ac.buildFail()
n := len(target)
f := make([]int, n+1)
cur := ac.root
for i, b := range target {
cur = cur.son[b-'a'] // 如果没有匹配相当于移动到 fail 的 son[b-'a']
i++
f[i] = math.MaxInt / 2
if cur.len > 0 { // 匹配到了一个尽可能长的 words[k]
f[i] = min(f[i], f[i-cur.len]+cur.cost)
}
// 还可能匹配其余更短的 words[k],要在 last 链上找
for match := cur.last; match != ac.root; match = match.last {
f[i] = min(f[i], f[i-match.len]+match.cost)
}
}
if f[n] == math.MaxInt/2 {
return -1
}
return f[n]
}
func main() {
target := "abcdef"
words := []string{"abdef","abc","d","def","ef"}
costs := []int{100,1,1,10,5}
result := minimumCost(target,words,costs)
fmt.Println(result)
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
- 2025-03-07:网格图操作后的最大分数。给定一个 n x n 的二维矩阵 grid,初始时所有格子均为白色。你可以进行操作
- 2025-03-06:给定一个长度为 n 的整数组 nums,其中 n 是偶数,同时还有一个整数 k。 你可以进行一些操作,每次
- 绝了!k3s (k8s) 安装 ollama 运行 deepseek 全流程揭秘,yaml全公开
- 2025-03-04:求出硬币游戏的赢家。用go语言,给定两个正整数 x 和 y,分别代表75元和10元硬币的数量。 Alice
- 2025-03-03:切蛋糕的最小总开销Ⅱ。用go语言,你有一个大小为 m x n 的矩形蛋糕,需要将其切割成 1 x 1 的小
评论(0)