#Decision Tree

class Node:
    def __init__(self, attribute=None, left=None, right=None):
        self.attribute = attribute
        self.left = left
        self.right = right

def decision_tree(X, T, K):
    n = len(X)  # Number of objects
    m = len(T)  # Number of tests

    # Initialize the dynamic programming table
    dp = [[float('inf')] * (K + 1) for _ in range(n + 1)]
    dp[0][0] = 0

    # Build the decision tree using dynamic programming
    for i in range(1, n + 1):
        for k in range(K + 1):
            for j in range(m):
                if satisfies_test(X[i - 1], T[j]):
                    left_length = min(k, dp[i - 1][k] + 1)
                    right_length = min(k - 1, dp[i - 1][k - 1] + 1)
                    dp[i][k] = min(dp[i][k], max(left_length, right_length))

    # Reconstruct the decision tree
    root = reconstruct_tree(X, T, dp, K)

    return root

def satisfies_test(x, test):
    for i in range(len(x)):
        if test[i] != 'x' and test[i] != x[i]:
            return False
    return True

def reconstruct_tree(X, T, dp, K):
    n = len(X)  # Number of objects
    m = len(T)  # Number of tests

    root = Node()

    # Reconstruct the decision tree using backtracking
    for i in range(n, 0, -1):
        for j in range(m):
            if satisfies_test(X[i - 1], T[j]):
                left_length = min(K, dp[i - 1][K])
                right_length = min(K - 1, dp[i - 1][K - 1])
                if max(left_length, right_length) == dp[i][K]:
                    root.attribute = T[j]
                    root.left = reconstruct_tree(X[:i - 1], T, dp, left_length)
                    root.right = reconstruct_tree(X[:i - 1], T, dp, right_length)
                    return root

    return root

# Example usage
if __name__ == '__main__':
    # Example instance
    X = ['A', 'B', 'C', 'D']
    T = [['0', 'x', '1', '0'], ['x', '1', '0', '1']]
    K = 3

    # Solve the "Decision Tree" problem
    root = decision_tree(X, T, K)

    # Print the decision tree
    print("Decision Tree:")
    print_tree(root)

def print_tree(node, indent=''):
    if node.attribute is None:
        print(indent + "Leaf: " + str(node.label))
    else:
        print(indent + "Attribute: " + str(node.attribute))
        print(indent + "Left:")
        print_tree(node.left, indent + "  ")
        print(indent + "Right:")
        print_tree(node.right, indent + "  ")

