Satisfiability of Equality Equations

Updated on 23 June, 2025
Satisfiability of Equality Equations header image

Problem Statement

You are provided with an array called equations, consisting of strings that represent relationships between single-letter variable names. Each string within this array is exactly four characters long and encapsulates an equality or inequality relation using the format "xi==yi" or "xi!=yi". The characters xi and yi in these strings are lowercase alphabets that signify variable names and might not necessarily be distinct from each other.

Your challenge is to determine if there exists an assignment of integers to these variable names such that all the given equations can simultaneously hold true. The function should return true if such an assignment is possible and false otherwise.

Examples

Example 1

Input:

equations = ["a==b","b!=a"]

Output:

false

Explanation:

If we assign say, a = 1 and b = 1, then the first equation is satisfied, but not the second.
There is no way to assign the variables to satisfy both equations.

Example 2

Input:

equations = ["b==a","a==b"]

Output:

true

Explanation:

We could assign a = 1 and b = 1 to satisfy both equations.

Constraints

  • 1 <= equations.length <= 500
  • equations[i].length == 4
  • equations[i][0] is a lowercase letter.
  • equations[i][1] is either '=' or '!'.
  • equations[i][2] is '='.
  • equations[i][3] is a lowercase letter.

Approach and Intuition

The task described above essentially requires checking the consistency of a set of equality and inequality relationships among variables. Understanding the problem can be broken into smaller parts:

  1. Parse the given equations to classify relationships:
    • Extract all "equality" (==) relations.
    • Extract all "inequality" (!=) relations.
  2. Utilize a Disjoint Set Union (DSU) or union-find data structure:
    • This structure will help manage and merge groups or sets of variables that are defined to be equal.
  3. Process each equality:
    • Union any variables that are declared as equal. For instance, for equation "a==b", merge set containing a with set containing b.
  4. Verify inequalities:
    • After processing all equalities, check each inequality to see if the involved variables are in the same set. If so, it contradicts the inequality, and you immediately return false.
    • If none of the inequalities cause a contradiction (i.e., the involved variables in each inequality belong to different sets), then return true.

Through this method, the primary power lies in efficiently managing and querying groups of 'equal' variables, ensuring that no 'inequality' relationship within these groups exists. Simplifying these relationships and ensuring consistency enables us to determine the validity of variable assignments as described in the problem statement.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    bool canResolve(vector<string>& eqns) {
        const int ALPHABET_COUNT = 26;
        vector<int> parent(ALPHABET_COUNT);
        iota(parent.begin(), parent.end(), 0);

        function<int(int)> findParent = [&](int node) {
            if (parent[node] != node) {
                parent[node] = findParent(parent[node]);
            }
            return parent[node];
        };

        auto merge = [&](int node1, int node2) {
            int root1 = findParent(node1), root2 = findParent(node2);
            parent[root1] = root2;
        };

        for (string& eqn : eqns) {
            if (eqn[1] == '=') {
                int char1 = eqn[0] - 'a';
                int char2 = eqn[3] - 'a';
                merge(char1, char2);
            }
        }

        for (string& eqn : eqns) {
            if (eqn[1] == '!') {
                int char1 = eqn[0] - 'a';
                int char2 = eqn[3] - 'a';
                if (findParent(char1) == findParent(char2)) {
                    return false;
                }
            }
        }
        return true;
    }
};

The provided C++ solution addresses the problem of determining the satisfiability of equality equations. This solution utilizes the Union-Find algorithm to manage the relationships between variables represented by letters. Here's a concise breakdown of how the code tackles the problem:

  • Initialization: The program starts by setting up a parent array of size 26, one for each lowercase English letter. The initialization uses iota to set each letter's parent to itself, establishing each as its own independent set initially.

  • Union-Find Functions:

    • findParent: A recursive function that locates the representative or root of the set a given node belongs to. It implements path compression by updating the parent of each node during the recursion, making future queries faster.
    • merge: A function to unite two sets. It finds the roots of the sets containing each node and sets one root to be the parent of the other, effectively merging the sets.
  • Processing Equalities: The first loop iterates over all equations. For each equation asserting equality (a == b), it identifies the variables, converts them to their corresponding index (0 for 'a' through 25 for 'z'), and merges their sets.

  • Processing Inequalities: The second loop processes all equations again, this time focusing on inequalities (a != b). It checks whether the two variables that should not be equal are in the same set by comparing their root parents. If they are in the same set, the function returns false, indicating the equations are not satisfiable collectively.

  • Final Output: If no conflicting inequality is found after checking all equations, the function returns true, confirming the equations are satisfactorily resolvable.

This method efficiently handles the problem by using a combination of union-find operations, ensuring all connected components (equivalence relations) are managed properly and checked against inequalities to ensure overall consistency.

java
class Solution {
    public boolean canBeEqual(String[] equations) {
        int[] parent = new int[26];
        for (int i = 0; i < 26; i++) {
            parent[i] = i;
        }

        for (String eq : equations) {
            if (eq.charAt(1) == '=') {
                int first = eq.charAt(0) - 'a';
                int second = eq.charAt(3) - 'a';
                link(parent, first, second);
            }
        }

        for (String eq : equations) {
            if (eq.charAt(1) == '!') {
                int first = eq.charAt(0) - 'a';
                int second = eq.charAt(3) - 'a';
                if (locate(parent, first) == locate(parent, second))
                    return false;
            }
        }

        return true;
    }

    private static int locate(int[] parent, int idx) {
        if (parent[idx] != idx)
            parent[idx] = locate(parent, parent[idx]);
        return parent[idx];
    }

    private static void link(int[] parent, int first, int second) {
        int rootFirst = locate(parent, first);
        int rootSecond = locate(parent, second);
        if (rootFirst != rootSecond)
            parent[rootFirst] = rootSecond;
    }
}

The problem "Satisfiability of Equality Equations" is solved using a solution that involves the Union-Find data structure, specifically designed in Java. Here's an overview of how the solution addresses the problem:

  • Initialization and Union Operations:

    • Start by initializing an array parent where each position represents a unique character, hence sized at 26 for each letter of the alphabet (assuming lowercase English letters).
    • Iterate through each equation. For those that indicate equality (==), unify the sets of the given characters using the link function.
  • Path Compression in Find Operation:

    • Implement a locate function that seeks the root of a character while implementing path compression. Path compression flattens the structure of the tree by making every node point directly to the root when accessed.
  • Linking Characters:

    • The link function connects two characters by finding their roots. If the roots differ, one root is redirected to the other, effectively stating that those characters are equal according to the union-find structure.
  • Checking Inequality Constraints:

    • After processing the equality equations, iterate through the equations again for those indicating inequality (!=).
    • For each inequality equation, check if both characters have the same root. If they do, return false because this would contradict the inequality statement as per the already determined linkages.
  • Completing the Computation:

    • If all inequality checks pass without any contradictions, return true.

This approach efficiently determines whether all equations can be simultaneously satisfied, leveraging the power of the union-find structure with path compression and union by rank techniques to optimize the operations.

python
class Solution:
    def canBeEqual(self, expressions: List[str]) -> bool:
        parent = list(range(26))

        def find_node(n):
            if n != parent[n]:
                parent[n] = find_node(parent[n])
            return parent[n]

        def connect(x, y):
            root_x, root_y = find_node(x), find_node(y)
            parent[root_x] = root_y

        for expr in expressions:
            if expr[1] == '=':
                index1, index2 = ord(expr[0]) - ord('a'), ord(expr[3]) - ord('a')
                connect(index1, index2)

        for expr in expressions:
            if expr[1] == '!':
                index1, index2 = ord(expr[0]) - ord('a'), ord(expr[3]) - ord('a')
                if find_node(index1) == find_node(index2):
                    return False
        return True

This Python solution addresses solving the problem of determining whether a set of equality equations is satisfiable. The equations consist of variables represented by lowercase letters and are either equal (==) or not equal (!=). The approach leverages the Union-Find data structure (also known as Disjoint Set Union, DSU) to efficiently manage and query the relationships between variables.

The canBeEqual function checks the satisfiability using the following steps:

  1. Initialize the parent list where each index represents a unique character from a to z mapped to integers 0 to 25. Each character/integer initially points to itself, implying no relationships are established yet.

  2. Define the find_node function that finds the root of a set for a given node. It implements path compression to flatten the structure, which improves the efficiency of future operations.

  3. Define the connect function to union two sets. It merges the sets of two characters if they are connected by an '==' equation.

  4. Process all expressions that denote equality ('==') to build clusters of connected characters by calling the connect function.

  5. Finally, iterate through expressions denoting inequality ('!='). If any two characters connected by '!=' are found to belong to the same cluster (i.e., have the same root), the function returns False, as the equations are not satisfiable. If no such case occurs, it returns True after all checks, indicating the equations can coexist without contradiction.

By employing the Union-Find structure with path compression and union by rank, this solution ensures optimal performance for the given problem, especially with large input sizes.

Comments

No comments yet.