After having looked at the original problem, I think you have misunderstood it.
This question is about an AND/OR
tree where the nodes at the deepest level are AND
nodes. The logical operatives at the other nodes are determined by this factor - we do not know if they are AND
or OR
nodes initially, we're only given that the nodes at the deepest level are AND
nodes - so the nodes at the next higher level are OR
nodes, and the next higher level are AND
nodes, and so and so on... the logical operatives interchange between different depths of the tree. This will become clear if you look at the sample AND/OR
tree they have provided.
The way I'd approach this problem is to first figure out the logical connective for the root node. This can be done with a single scan over the expression and keeping track of the number of parentheses. Note that each ()
corresponds to a new node in the tree (the next level of the tree). For an example, consider the expression:
((F(TF))(TF))
When you walk across this expression, first we encounter 3 opening parentheses, 2 closing, 1 opening and then finally 2 closing. If you take the maximum number of parentheses that were open at any given time during this walk, it'll be the maximum depth of this AND/OR
tree (3
in the above example).
So what does this mean? If the depth of the tree is odd, then the root node is an AND
node, otherwise the root is an OR
node (because the connectives alternate).
Once you know the connective of the root node, you can evaluate this expression using a simple stack based machine. We need to keep in mind that every time we open or close a parentheses, we need to flip the connective. Here's how the above expression gets evaluated:
AND |- (•(F(TF))(TF))
Notice that the bullet indicates where we are at the expression (like top of the stack). Then we proceed like below:
OR |- ((•F(TF))(TF)) // flipped the connective because we jumped a node
OR |- ((F•(TF))(TF)) // nothing to evaluate on the current node, push F
AND |- ((F(•TF))(TF))
AND |- ((F(T•F))(TF))
AND |- ((F(TF•))(TF))
AND |- ((F(F•))(TF)) // Two booleans on top, T AND F = F (reduce)
OR |- ((F(F)•)(TF)) // Jumped out of a node, flip the sign
OR |- ((FF•)(TF)) // Completely evaluated node on top, (F) = F (reduce)
OR |- ((F•)(TF)) // Two booleans on top, F OR F = F (reduce)
AND |- ((F)•(TF))
AND |- (F•(TF))
OR |- (F(•TF))
OR |- (F(T•F))
OR |- (F(TF•))
OR |- (F(T•))
AND |- (F(T)•)
AND |- (FT•)
AND |- (F•)
So you get the final answer as F
. This has some relation to shift-reduce parsing but the reductions in this case depend on the current depth of the AST we're operating at. I hope you'll be able to translate this idea into code (you'll need a stack and a global variable for keeping track of the current logical operative in force).
Finally, thank you for introducing that site. You might also like this site.