等式方程的可满足性
题目描述
给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:“a==b” 或 “a!=b”。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。
只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。
解题思路:并查集
class Solution {
class UF {
// 记录连通分量个数
private int count;
// 存储若干棵树
private int[] parent;
// 记录树的“重量”
private int[] size;
public UF(int n) {
this.count = n;
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
}
/* 将 p 和 q 连通 */
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ)
return;
// 小树接到大树下面,较平衡
if (size[rootP] > size[rootQ]) {
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
} else {
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
}
count--;
}
/* 判断 p 和 q 是否互相连通 */
public boolean connected(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
// 处于同一棵树上的节点,相互连通
return rootP == rootQ;
}
/* 返回节点 x 的根节点 */
private int find(int x) {
while (parent[x] != x) {
// 进行路径压缩
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
public int count() {
return count;
}
}
public boolean equationsPossible(String[] equations) {
// 26 个英文字母
UF uf = new UF(26);
// 先让相等的字母形成连通分量
for (String eq : equations) {
if (eq.charAt(1) == '=') {
char x = eq.charAt(0);
char y = eq.charAt(3);
uf.union(x - 'a', y - 'a');
}
}
// 检查不等关系是否打破相等关系的连通性
for (String eq : equations) {
if (eq.charAt(1) == '!') {
char x = eq.charAt(0);
char y = eq.charAt(3);
// 如果相等关系成立,就是逻辑冲突
if (uf.connected(x - 'a', y - 'a')) {
return false;
}
}
}
return true;
}
}
连通分量+dfs
思路
所有相互等于的变量能组成一个联通分量。举一个例子,如果 a=b, b=c, c=d,那么 a, b, c, d 就在同一个联通分量中,因为它们必须相等。
算法
第一步,我们基于给定的等式,用深度优先遍历将每一个变量按照联通分量染色。
将联通分量染色之后,我们分析形如 a != b 的不等式。如果两个分量有相同的颜色,那么它们一定相等,因此如果说它们不相等的话,就一定无法满足给定的方程组。
否则,我们的染色就可以看作是一组能满足方程组约束的方案,所以结果是 true。
class Solution {
public boolean equationsPossible(String[] equations) {
ArrayList<Integer>[] graph = new ArrayList[26];
for (int i = 0; i < 26; ++i)
graph[i] = new ArrayList();
for (String eqn: equations) {
if (eqn.charAt(1) == '=') {
int x = eqn.charAt(0) - 'a';
int y = eqn.charAt(3) - 'a';
graph[x].add(y);
graph[y].add(x);
}
}
int[] color = new int[26];
int t = 0;
for (int start = 0; start < 26; ++start) {
if (color[start] == 0) {
t++;
Stack<Integer> stack = new Stack();
stack.push(start);
while (!stack.isEmpty()) {
int node = stack.pop();
for (int nei: graph[node]) {
if (color[nei] == 0) {
color[nei] = t;
stack.push(nei);
}
}
}
}
}
for (String eqn: equations) {
if (eqn.charAt(1) == '!') {
int x = eqn.charAt(0) - 'a';
int y = eqn.charAt(3) - 'a';
if (x == y || color[x] != 0 && color[x] == color[y])
return false;
}
}
return true;
}
}
复杂度分析
-
时间复杂度: O(N),其中 N 是方程组 equations 的数量
-
空间复杂度: O(1),认为字母表的大小是 O(1) 的。