ARTS LeetCode 990: 等式方程的可满足性

186 阅读4分钟

继续刷LeetCode,做做脑力运动,今天继续使用go来做开发,随机刷了一道编号为990的题:satisfiability of equality equations,中文名叫等式方程的可满足性,难度:中等。

难度确实高了一些,需要的解题步骤复杂了不少,开始拆解错了问题,耽搁了一些功夫,整体来说go语言的熟悉程度影响最大,go官方没有set以及初始化复杂结构的方式也不是好理解,这个后续得理理,或许需要看些源码学习一下了。

官方一共181个case,有几个就是为了测试性能的,先放一个最终结果吧

执行用时 : 28 ms, 在所有 Go 提交中击败了 76.19% 的用户 内存消耗 : 6.1 MB, 在所有 Go 提交中击败了 100.00% 的用户

详细信息参考这里 等式方程的可满足性 - 提交记录 - 力扣 (LeetCode)

简单说一下解题思路,整个过程分为两个阶段:

  • 首先把输入的信息转化为方便遍历的结构allData,针对每一个等式,记录该变量的等价元素集合以及不等价元素集合,这里有以下注意点
    • 在go中没有找到顺手的集合,用map代替
    • 如果遇到a==a直接跳过
    • 如果遇到a!=a的情况,可以直接返回处理
    • 这里要主要双向处理,即所有关系是对等
    • 因为遇到一个语法障碍,导致初始化部分的代码非常别扭,后面再仔细说
  • 然后,遍历整个allData,看看检查其中每个元素是否满足所有的等式方程,这里的方法也比较简单粗暴,把不想等集合中每个元素看看是否也存在相等集合中,如果存在,则返回不满足,这里只有一个注意点:
    • 因为等式的传递性,所以要递归检查所有等式集合中的下一级,这个递归只需要注意递归跳出条件就好

最后通过测试的代码如下。

// Relation for equal and no-equal
type Relation struct {
	eq map[byte]bool
	ne map[byte]bool
}

func equationsPossible(equations []string) bool {
	fmt.Println(equations)
	var allData = make(map[byte]Relation)
	for _, equation := range equations {
		e1 := equation[0]
		e2 := equation[3]
		if equation[1] == '=' {
			if e1 == e2 {
				continue
			}
			a := Relation{}
			a.eq = map[byte]bool{e2: true}
			a.ne = allData[e1].ne
			for k := range allData[e1].eq {
				a.eq[k] = true
			}
			allData[e1] = a

			b := Relation{}
			b.eq = map[byte]bool{e1: true}
			for k := range allData[e2].eq {
				b.eq[k] = true
			}
			b.ne = allData[e2].ne
			allData[e2] = b
		} else {
			if e1 == e2 {
				return false
			}
			a := Relation{}
			a.ne = map[byte]bool{e2: true}
			a.eq = allData[e1].eq
			for k := range allData[e1].ne {
				a.ne[k] = true
			}
			allData[e1] = a

			b := Relation{}
			b.ne = map[byte]bool{e1: true}
			for k := range allData[e2].ne {
				b.ne[k] = true
			}
			b.eq = allData[e2].eq
			allData[e2] = b
		}
	}

	// fmt.Println(allData)
	for e1, v := range allData {
		for e2 := range v.ne {
			if checkFailed(e1, e2, allData, map[byte]bool{}) {
				return false
			}
			if checkFailed(e2, e1, allData, map[byte]bool{}) {
				return false
			}
		}
	}
	return true
}

func checkFailed(e1 byte, e2 byte, allData map[byte]Relation, path map[byte]bool) bool {
	if _, exists := path[e1]; exists {
		return false
	}
	if _, exists := allData[e1].eq[e2]; exists {
		return true
	}
	for k := range allData[e1].eq {
		path[e1] = true
		if checkFailed(k, e2, allData, path) {
			return true
		}
	}
	return false
}

还有测试用例,大部分取自官方测试用例集合,包含大多数典型情况。

package main

import "testing"

func Test_rob(t *testing.T) {
	type args struct {
		nums []int
	}

	tests := []struct {
		name string
		args args
		want int
	}{
		{"TestCase 1", args{[]string{"a==b"}}, true},
		{"TestCase 2", args{[]string{"a==b", "b!=a"}}, false},
		{"TestCase 3", args{[]string{"b==a", "a==b"}}, true},
		{"TestCase 4", args{[]string{"a==b", "b==c", "a==c"}}, true},
		{"TestCase 5", args{[]string{"a==b", "b!=c", "c==a"}}, false},
		{"TestCase 6", args{[]string{"c==c", "b==d", "x!=z"}}, true},
		{"TestCase 7", args{[]string{"a!=b", "b!=c", "c!=a"}}, true},
		{"TestCase 8", args{[]string{"a==a"}}, true},
		{"TestCase 9", args{[]string{"a!=a"}}, false},
		{"TestCase 9", args{[]string{"a==b", "e==c", "b==c", "a!=e"}}, false},
		{"TestCase 10", args{[]string{"b==b", "b==e", "e==c", "d!=e"}}, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := rob(tt.args.nums); got != tt.want {
				t.Errorf("rob() = %v, want %v", got, tt.want)
			}
		})
	}
}

收获

先记录一下遇到的初始化复杂数据结构的问题,如果像下面这些代码这么写,编译器会报错cannot assign to struct field allData['a'].eq in mapgo

type Relation struct {
	eq map[byte]bool
	ne map[byte]bool
}
var allData = make(map[byte]Relation)
allData['a'] = Relation{}
allData['a'].eq = make(map[byte]bool)

如果写成下面在我看来等价的形式就没问题,直觉上很奇怪

type Relation struct {
	eq map[byte]bool
	ne map[byte]bool
}
var allData = make(map[byte]Relation)
a := Relation{}
a.eq = map[byte]bool{e2: true}
allData['a'] = a

或者下面的写法也可以

allData[e1] = Relation{map[byte]bool{}, map[byte]bool{}}
allData[e1].eq['b'] = true

看了些关于这个错误的解释、Go maps in action - The Go Blog 、github上的issueproposal: spec: cannot assign to a field of a map element directly: m["foo"].f = x · Issue #3117 · golang/go 以及语言规范中关于赋值部分,模糊有点感觉大体能理解,但是感觉是没处理好的一个地方。

在go中map是个指针类型,在这种情况下不能直接赋值,下面这个例子更清晰,貌似这种写法更好一些

allData[e1] = Relation{map[byte]bool{}, map[byte]bool{}}
if thisData, ok := allData[e1]; ok {
    thisData.eq = map[byte]bool{}
    allData[e1] = thisData
} else {
    // 初始化该元素
}

看来后面得多看看源码,熟悉一下编码规范了。

Reference