2025-02-26:最小代价构造字符串。用go语言,给定一个目标字符串 target、一个字符串数组 words 和一个整数数

举报
福大大架构师每日一题 发表于 2025/02/26 15:21:03 2025/02/26
52 0 1
【摘要】 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:

chatgpt

题目来自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
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。