var isVisit map[int]int // 保留已经得到的结果,该结构相当于一个备忘录
// 记忆化搜索函数调用者
func memorySearchCaller() {
/* 1. 进行一些预处理 */
/* 2. 开始调用记忆化搜索函数,返回记忆化搜索结果 */
}
// 记忆化搜索函数
func memorySearch() {
/* 3. 判断是否需要返回结果以及进行一些剪枝 (特殊情况处理) */
// 如果该问题已经求解过了,那么直接返回结果
if x, ok := isVisit[key]; ok {
return x
}
/* 4. 如果没求解,则继续调用记忆化搜索函数,得出结果 (一般情况处理) */
// 记录该问题的结果,加入备忘录
isVisit[key] = ans
return ans
}
583. 两个字符串的删除操作
var isVisit map[string]int // 保留已经得到的结果,该结构相当于一个备忘录
// 记忆化搜索函数调用者
func minDistance(word1 string, word2 string) int {
/* 1. 进行一些预处理 */
isVisit = make(map[string]int)
/* 2. 开始调用记忆化搜索函数,返回记忆化搜索结果 */
return minDistanceExec(word1, word2)
}
// 记忆化搜索函数
func minDistanceExec(word1 string, word2 string) int {
/* 3. 判断是否需要返回结果以及进行一些剪枝 (特殊情况处理) */
if len(word1) == 0 {
return len(word2)
}
if len(word2) == 0 {
return len(word1)
}
// 如果该问题已经求解过了,那么直接返回结果
hashVal := hash(word1, word2)
if x, ok := isVisit[hashVal]; ok {
return x
}
/* 4. 如果没求解,则继续调用记忆化搜索函数,得出结果 (一般情况处理) */
ans := 0
if word1[len(word1)-1] == word2[len(word2)-1] {
ans = minDistanceExec(word1[:len(word1)-1], word2[:len(word2)-1])
} else {
a := minDistanceExec(word1[:len(word1)-1], word2)
b := minDistanceExec(word1, word2[:len(word2)-1])
ans = min(a, b) + 1
}
// 记录该问题的结果,加入备忘录
isVisit[hashVal] = ans
return ans
}
// 由于备忘录的键值是 1 个字符串,而记忆化搜索函数需要 2 个字符串参数才能唯一标识一个子问题,
// 所以,这里采用哈希的方式,把两个参数进行哈希,生成一个键值来唯一的标识这个参数组合,
// 即: 用「1个字符串」 唯一标识 「1个子问题」。
func hash(a, b string) string {
return a + "|" + b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
10. 正则表达式匹配
var hasResult map[string]bool // 保留已经得到的结果,该结构相当于一个备忘录
// 记忆化搜索函数调用者
func isMatch(s string, p string) bool {
/* 1. 进行一些预处理 */
hasResult = make(map[string]bool)
/* 2. 开始调用记忆化搜索函数,返回记忆化搜索结果 */
return isMatchExec(s, p)
}
// 记忆化搜索函数
func isMatchExec(s string, p string) bool {
/* 3. 判断是否需要返回结果以及进行一些剪枝 (特殊情况处理) */
if s == p {
return true
}
if p == "" {
return s == ""
}
ends, endp := len(s)-1, len(p)-1
if s == "" {
if p[endp] == '*' {
return isMatchExec(s, p[:endp-1])
}
return false
}
// 如果该问题已经求解过了,那么直接返回结果
key := hash(s, p)
if x, ok := hasResult[key]; ok {
return x
}
/* 4. 如果没求解,则继续调用记忆化搜索函数,得出结果 (一般情况处理) */
ans := false
if s[ends] == p[endp] || p[endp] == '.' {
ans = isMatchExec(s[:ends], p[:endp])
} else {
if p[endp] == '*' {
if p[endp-1] == s[ends] || p[endp-1] == '.' {
ans = isMatchExec(s, p[:endp]) || isMatchExec(s[:ends], p) || isMatchExec(s, p[:endp-1])
} else {
ans = isMatchExec(s, p[:endp-1])
}
}
}
// 记录该问题的结果,加入备忘录
hasResult[key] = ans
return ans
}
// 由于备忘录的键值是 1 个字符串,而记忆化搜索函数需要 2 个字符串参数才能唯一标识一个子问题,
// 所以,这里采用哈希的方式,把两个参数进行哈希,生成一个键值来唯一的标识这个参数组合,
// 即: 用「1个字符串」 唯一标识 「1个子问题」。
func hash(s, p string) string {
return s + "|" + p
}
72. 编辑距离
var isVisit map[string]int // 保留已经得到的结果,该结构相当于一个备忘录
// 记忆化搜索函数调用者
func minDistance(word1 string, word2 string) int {
/* 1. 进行一些预处理 */
isVisit = make(map[string]int)
/* 2. 开始调用记忆化搜索函数,返回记忆化搜索结果 */
return minDistanceExec(word1, word2)
}
// 记忆化搜索函数
func minDistanceExec(word1 string, word2 string) int {
/* 3. 判断是否需要返回结果以及进行一些剪枝 (特殊情况处理) */
if len(word1) == 0 {
return len(word2)
}
if len(word2) == 0 {
return len(word1)
}
// 如果该问题已经求解过了,那么直接返回结果
hashVal := hash(word1, word2)
if x, ok := isVisit[hashVal]; ok {
return x
}
/* 4. 如果没求解,则继续调用记忆化搜索函数,得出结果 (一般情况处理) */
ans := 0
if word1[len(word1)-1] == word2[len(word2)-1] {
ans = minDistanceExec(word1[:len(word1)-1], word2[:len(word2)-1])
} else {
a := minDistanceExec(word1[:len(word1)-1], word2)
b := minDistanceExec(word1[:len(word1)-1], word2[:len(word2)-1])
c := minDistanceExec(word1, word2[:len(word2)-1])
ans = min(a, b, c) + 1
}
// 记录该问题的结果,加入备忘录
isVisit[hashVal] = ans
return ans
}
// 由于备忘录的键值是 1 个字符串,而记忆化搜索函数需要 2 个字符串参数才能唯一标识一个子问题,
// 所以,这里采用哈希的方式,把两个参数进行哈希,生成一个键值来唯一的标识这个参数组合,
// 即: 用「1个字符串」 唯一标识 「1个子问题」。
func hash(a, b string) string {
return a + "|" + b
}
// 这里我重写了min函数,让它可以计算n个参数的最小值
func min(arr ...int) int {
if len(arr) == 1 {
return arr[0]
}
a, b := arr[0], min(arr[1:]...)
if a > b {
return b
}
return a
}
516. 最长回文子序列
375. 猜数字大小 Ⅱ
var inf int // 无穷大
var amount map[int]int // 保留已经得到的结果,该结构相当于一个备忘录
// 记忆化搜索函数调用者
func getMoneyAmount(n int) int {
/* 1. 进行一些预处理 */
amount = make(map[int]int)
inf = 100000000000
/* 2. 开始调用记忆化搜索函数,返回记忆化搜索结果 */
return getMoneyAmountExec(1, n)
}
// 记忆化搜索函数
func getMoneyAmountExec(l, r int) int {
/* 3. 判断是否需要返回结果以及进行一些剪枝 (特殊情况处理) */
if l >= r {
return 0
}
// 如果该问题已经求解过了,那么直接返回结果
hashNumber := hash(l,r)
if x, ok := amount[hashNumber]; ok {
return x
}
/* 4. 如果没求解,则继续调用记忆化搜索函数,得出结果 (一般情况处理) */
ans := inf
for i := l; i <= r; i++ {
left := getMoneyAmountExec(l, i-1)
right := getMoneyAmountExec(i+1, r)
ans = min(ans, max(left, right)+i)
}
// 记录该问题的结果,加入备忘录
amount[hashNumber] = ans
return ans
}
// 由于备忘录的键值是 1 个整数,而记忆化搜索函数需要 2 个整数参数才能唯一标识一个子问题,
// 所以,这里采用哈希的方式,把两个参数进行哈希,生成一个键值来唯一的标识这个参数组合,
// 即: 用「1个整数」 唯一标识 「1个子问题」。
func hash(l,r int) int{
off := 10
return (r << off) | l
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
678. 有效的括号字符串
var isVisit map[int]bool // 保留已经得到的结果,该结构相当于一个备忘录
// 记忆化搜索函数调用者
func checkValidString(s string) bool {
/* 1. 进行一些预处理 */
isVisit = make(map[int]bool)
/* 2. 开始调用记忆化搜索函数,返回记忆化搜索结果 */
return checkValidStringExec(s, len(s)-1, 0, 0)
}
// 记忆化搜索函数调用者
// s[: nowIndex+1]为当前处理的字符串
// left, right 表示此时的左右括号数量
func checkValidStringExec(s string, nowIndex int, left, right int) bool {
/* 3. 判断是否需要返回结果以及进行一些剪枝 (特殊情况处理) */
if nowIndex == -1 {
return left == right
}
if left > right {
return false
}
// 如果该问题已经求解过了,那么直接返回结果
hashNumber := hash(nowIndex, left, right)
if x, ok := isVisit[hashNumber]; ok {
return x
}
/* 4. 如果没求解,则继续调用记忆化搜索函数,得出结果 (一般情况处理) */
ans := false
lastChar := s[nowIndex]
if lastChar == '(' || lastChar == '*'{
ans = ans || checkValidStringExec(s, nowIndex-1, left+1, right)
}
if lastChar == ')' || lastChar == '*'{
ans = ans || checkValidStringExec(s, nowIndex-1, left, right+1)
}
if lastChar == '*' {
ans = ans || checkValidStringExec(s, nowIndex-1, left, right)
}
// 记录该问题的结果,加入备忘录
isVisit[hashNumber] = ans
return ans
}
// 由于备忘录的键值是 1 个整数,而记忆化搜索函数需要 3 个整数参数才能唯一标识一个子问题,
// 所以,这里采用哈希的方式,把三个参数进行哈希,生成一个键值来唯一的标识这个参数组合,
// 即: 用「1个数字」 唯一标识 「1个子问题」。
func hash(a, b, c int) int {
return (a << 20) + (b << 10) + c
}