I am assuming that a given input can only appear in one leaf (otherwise the problem again becomes boolean satisfiability--despite the tree structure and restricted operators--by De Morgan's laws).
I believe the answer can be computed without a data-structure. You don't need to enumerate the solutions, afterall. I think the recursion step takes 2 parameters: the node to process, and the target output to be produced by that node, and it returns two values: the number of ways that achieve the target for the node, and the total number of leaves below the node. Both of these can be obtained via a single depth-first traversal over the tree.
Here's pseudo-code with the idea:
(int ways, int leaves) = recurse(Node n, bool target) {
//Base case of recursion:
if(n.type == LEAF) {
return (1, 1);
}
else if(n.type == NOT) {
//for a NOT, we just invert the target.
return recurse(n.child[0], !target)
}
else if (n.type == AND) {
//AND is more complicated. First, count the ways that we can make each
//sub-expression evaluate to true, and the total number of leaves under
//both the left and right child:
(int left_true, int left_leaves) = recurse(n.child[0], true);
(int right_true, int right_leaves) = recurse(n.child[1], true);
//the total number of ways to make a true AND result is the product of
//the number of ways to make the left true, and the number of ways to
//make the right true.
int total_true_ways = left_true * right_true;
//the total number of leaves under this node is the sum of leaves under
//the left and right subtrees.
int total_leaves = left_leaves + right_leaves;
if(target == true) {
//if this node's target is 'true' we've already computed the number of
//ways to satisfy that.
return (total_true_ways, total_leaves);
}
else {
//The number of ways to make a 'false' is the total number of possible
//input combinations, less the number of input combinations that make
//a 'true'.
//The total number of possible input combinations is given by 2 to the
//power of the number of boolean inputs, which is given by the
//number of leaves below the node:
int num_possible_inputs = pow(2, total_leaves);
return ( num_possible_inputs - total_true_ways, total_leaves);
}
}
else {
throw internal error
}
}
Unfortunately, the running time can't be rigorously expressed in the number of leaves because your tree doesn't rule out arbitrarily-long chains of NOT operations. If we assume there are not back-to-back NOT limbs, then a maximal-depth tree is achieved by alternating layers of NOT and AND, leading to a tree with 2*ciel(log_2(n)+1)
layers and and 2n + 2(n-1)
total nodes. (Where n
is the number of leaf nodes.) So a depth-first traversal that touches each node once runs in O(n)
under the assumption of no back-to-back NOT operators.