We use cookies to ensure you have the best browsing experience on our website. Please read our cookie policy for more information about how we use cookies.
Here's my Java 8 code, which passes all test-cases, for Max Score of 50.
Couple of reasons why the code is so long: (1) Added a ton of verbose debug logging statements to trace & understand the logic. These are behind toggle switches & fine-tuned controls, (2) Implemented it via three approaches: (a) DFS Recursive, (b) DFS Iterative, (c) BFS (Iterative). And provided a quick way to toggle / cycle through all of them.
/////////////////////////////////////////////////////////////////////////////////////////////// SUMMARY: ///////////////////////////////////////////////////////////////////////////////////////////////// Approach (1A): DFS Recursive seems to work BEST on HackerRank test-cases. //// All Hacker-Rank Test-Cases pass OK. No wrong answers. No TLEs. //// //// Approach (1B): DFS Iterative fails ONE test-case (TLE). Everything else passes. //// Specifically, only TC-21 fails due to TLE. //// But I verified that Code Answer matches Expected Answer //// So it gives correct answer, but takes too much time. //// //// Approach (2): BFS Iterative fails ONE test-case (TLE). Everything else passes. //// Specifically, only TC-21 fails due to TLE. //// But I verified that Code Answer matches Expected Answer //// So it gives correct answer, but takes too much time. ///////////////////////////////////////////////////////////////////////////////////////////////classResult{// Verbose Debug Print Toggle SwitchesprivatestaticfinalbooleanvDbgL1=false;privatestaticfinalbooleanvDbgL2=false;privatestaticfinalbooleanvDbgL3=false;privatestaticfinalbooleanvDbgL4=false;privatestaticfinalbooleanvDbgL5=false;// Verbose Debug Print - Controls for LARGE Structures with LARGE num of elementsprivatestaticfinalintVDBG_MAX_SUBTREE_NODE_SIZE=50;privatestaticfinalintVDBG_MAX_SUBTREE_ADJ_GRAPH_SIZE=50;privatestaticfinalintVDBG_MAX_ADJ_GRAPH_SIZE=50;privatestaticfinalintVDBG_MAX_DISTINCT_SUB_TREE_ADJ_SET_SIZE=100;privatestaticfinalintVDBG_MAX_CANON_LEN=50;// Settings for Canonical Hash (String)privatestaticfinalStringSINGLE_NODE_SUBTREE_DEFAULT_CAN_HASH_STR="()";// Common Helper Class - representing Adjacency GraphprivatestaticclassAdjGraph{privateMap<Integer,List<Integer>>adjNodeEdgeLstMap;publicAdjGraph(){this.adjNodeEdgeLstMap=newLinkedHashMap<>();}publicAdjGraph(intnumNodes,List<List<Integer>>edges){AdjGraph.verifyNumNodesFromEdges(numNodes,edges);this.adjNodeEdgeLstMap=newLinkedHashMap<>();for(List<Integer>edge:edges){intu=edge.get(0),v=edge.get(1);this.addEdge(u,v);}if(vDbgL1){Stringmsg="Built ADJ has >"+this.adjNodeEdgeLstMap.size()+"< elements. ";if(this.adjNodeEdgeLstMap.size()<=VDBG_MAX_ADJ_GRAPH_SIZE){msg+="Built ADJ = >"+this.adjNodeEdgeLstMap+"<";}System.err.println(msg);}if(vDbgL2){if(this.adjNodeEdgeLstMap.size()<=VDBG_MAX_ADJ_GRAPH_SIZE){for(inti=0;i<this.adjNodeEdgeLstMap.size();i++){System.err.println("adj("+i+"): >"+this.adjNodeEdgeLstMap.get(i)+"<");}}}}voidaddEdge(intu,intv){if(!this.adjNodeEdgeLstMap.containsKey(u)){this.adjNodeEdgeLstMap.put(u,newArrayList<>());}this.adjNodeEdgeLstMap.get(u).add(v);if(!this.adjNodeEdgeLstMap.containsKey(v)){this.adjNodeEdgeLstMap.put(v,newArrayList<>());}this.adjNodeEdgeLstMap.get(v).add(u);}Map<Integer,List<Integer>>getAdj(){returnthis.adjNodeEdgeLstMap;}privatestaticvoidverifyNumNodesFromEdges(intnumNodes,List<List<Integer>>edges){intmaxNodeId=Integer.MIN_VALUE;for(List<Integer>edge:edges){maxNodeId=Math.max(maxNodeId,Math.max(edge.get(0),edge.get(1)));}if(maxNodeId!=numNodes){Stringmsg="UNEXPECTED! maxNodeId="+maxNodeId+" != numNodes="+numNodes+"! Invalid input!";System.err.println(msg);thrownewRuntimeException(msg);}if(edges.size()!=(numNodes-1)){Stringmsg="UNEXPECTED! edges.size()="+edges.size()+" != (numNodes - 1)="+(numNodes-1)+"! Invalid input!";System.err.println(msg);thrownewRuntimeException(msg);}}@OverridepublicStringtoString(){return"( A: ("+this.adjNodeEdgeLstMap+") )";}}// Common Helper Class for DFS Iterative (via Stack) & BFS Iterative (via Queue)privatestaticclassRelativeNodeDepth{intsrcNodeId;intcurNodeId;intdepthDist;RelativeNodeDepth(intsrcNodeId,intcurNodeId,intdepthDist){this.srcNodeId=srcNodeId;this.curNodeId=curNodeId;this.depthDist=depthDist;}publicStringtoString(){// return "(R: " + this.srcNodeId + " , C: " + this.curNodeId + // " , D: " + this.depthDist + ")";return"(C: "+this.curNodeId+" , D: "+this.depthDist+")";}}// Common Helper Enum - to switch between AlgorithmsprivatestaticenumAlgoChoice{DFS_Recursive("DFS Recursive"),DFS_Iterative("DFS Iterative"),BFS_Iterative("BFS Iterative");privatefinalStringdisplayName;// ConstructorAlgoChoice(StringdisplayName){this.displayName=displayName;}// Override toString to return your custom string@OverridepublicStringtoString(){returnthis.displayName;}}// Common Helper Function - To construct the Sub-Tree Adjacency Graph// Given the "Source / Original Adjacency Graph" + "Set of SubTree Nodes"privatestaticAdjGraphconstructSubTreeAdj(StringalgoName,intstartNodeId,AdjGraphsrcAdjGraph,Set<Integer>curSubTreeNodes){AdjGraphcurSubTreeAdjGraph=newAdjGraph();// Build induced adjacency for subtreefor(intnode:curSubTreeNodes){curSubTreeAdjGraph.adjNodeEdgeLstMap.put(node,newArrayList<>());}for(intnode:curSubTreeNodes){for(intnei:srcAdjGraph.adjNodeEdgeLstMap.get(node)){if(curSubTreeNodes.contains(nei)){curSubTreeAdjGraph.adjNodeEdgeLstMap.get(node).add(nei);}}}if(vDbgL5){Stringmsg="For "+algoName+" from Node "+startNodeId+" : ";msg+="curSubTreeNodes.size = >"+curSubTreeNodes.size()+"< | ";if(curSubTreeNodes.size()<=VDBG_MAX_SUBTREE_NODE_SIZE){msg+="curSubTreeNodes = >"+curSubTreeNodes+"< | ";}msg+="Constructed : curSubTreeAdjGraph.size = >"+curSubTreeAdjGraph.adjNodeEdgeLstMap.size()+"< | ";if(curSubTreeAdjGraph.adjNodeEdgeLstMap.size()<=VDBG_MAX_SUBTREE_ADJ_GRAPH_SIZE){msg+="Constructed : curSubTreeAdjGraph = >"+curSubTreeAdjGraph+"<";}System.err.println(msg);}returncurSubTreeAdjGraph;}// Common Helper Function - to find the Center of a GraphprivatestaticList<Integer>findCenters(AdjGraphsubTreeAdjGraph){intsize=subTreeAdjGraph.adjNodeEdgeLstMap.size();Map<Integer,Integer>degree=newHashMap<Integer,Integer>();Queue<Integer>leaves=newLinkedList<Integer>();for(intnode:subTreeAdjGraph.adjNodeEdgeLstMap.keySet()){intd=subTreeAdjGraph.adjNodeEdgeLstMap.get(node).size();degree.put(node,d);if(d<=1)leaves.add(node);}intremaining=size;while(remaining>2){intleafCount=leaves.size();remaining-=leafCount;for(inti=0;i<leafCount;i++){intleaf=leaves.poll();for(intnei:subTreeAdjGraph.adjNodeEdgeLstMap.get(leaf)){degree.put(nei,degree.get(nei)-1);if(degree.get(nei)==1){leaves.add(nei);}}}}returnnewArrayList<>(leaves);}// Common Helper Function - to construct the Normalized Minimum Canonical Form// which is a Normalized Hashed String Representation of the SubTree Graph// As taken from its CENTER. And if multiple centers, then lexicographically minimal.privatestaticStringfindNormMinForm(StringalgoName,intstartNodeId,AdjGraphcurSubTreeAdjGraph){// Find center(s) of this subtreeList<Integer>centers=findCenters(curSubTreeAdjGraph);// Compute canonical forms for each center and pick smallestStringnormMinForm=null;List<String>centerCanonForms=newArrayList<String>();// Compute minimal hash among all centersfor(intc:centers){StringcurCanonform=canonicalFormStr(c,0,curSubTreeAdjGraph);if(normMinForm==null||curCanonform.compareTo(normMinForm)<0){normMinForm=curCanonform;}}if(centers.size()>2||centerCanonForms.size()>2){StringerrMsg="UNEXPECTED! centers.size() = >"+centers.size()+"< | "+"centerCanonForms.size() = >"+centerCanonForms.size()+"<";System.err.println(errMsg);thrownewRuntimeException(errMsg);}if(vDbgL5){Stringmsg="For "+algoName+" from Node "+startNodeId+" :: "+"Subtree has >"+centers.size()+"< center(s) = >"+centers+"< "+"resulting in >"+centerCanonForms.size()+"< canonical forms";msg+=(normMinForm.length()<=VDBG_MAX_CANON_LEN?"< | And normMinForm = >"+normMinForm+"<":"< | And normMinForm has >"+normMinForm.length()+"< chars.");System.err.println(msg);}returnnormMinForm;}// Common Helper Function - to construct the Canonical Form (Hashed String Representation)// Given the Current SubTree's Adj Graph, and the node / parent IDs.privatestaticStringcanonicalFormStr(intnode,intparent,AdjGraphcurSubTreeAdjGraph){List<String>childrenForms=newArrayList<>();for(intnei:curSubTreeAdjGraph.adjNodeEdgeLstMap.get(node)){if(nei!=parent){childrenForms.add(canonicalFormStr(nei,node,curSubTreeAdjGraph));}}Collections.sort(childrenForms);return"("+String.join("",childrenForms)+")";}// Common Helper Function - to Print Lengthy Debug MessageprivatestaticvoiddebugPrintFinalDistinctSubTreeAdjSet(StringalgoName,Set<String>distinctSubTreeAdjSet){Stringmsg=algoName+" :: Final: distinctSubTreeAdjSet.size() = >"+distinctSubTreeAdjSet.size()+"<";if(distinctSubTreeAdjSet.size()<=VDBG_MAX_DISTINCT_SUB_TREE_ADJ_SET_SIZE){msg+="distinctSubTreeAdjSet = >[";intsetIdx=0;for(StringcurCanonForm:distinctSubTreeAdjSet){msg+=(curCanonForm.length()<=VDBG_MAX_CANON_LEN?curCanonForm:"<"+curCanonForm.length()+"_CHARS>");if(setIdx!=(distinctSubTreeAdjSet.size()-1)){msg+=", ";}setIdx++;}msg+="]<";}System.err.println(msg);}// Depth-First-Search (DFS) - Wrapper around Recursive MethodprivatestaticSet<Integer>dfsRecWrapper(intstartNodeId,AdjGraphadjGraph,intmaxNodeId,intdfsRadius,intremainingDepth){boolean[]visited=newboolean[maxNodeId+1];Set<Integer>curSubTreeNodeSet=newHashSet<Integer>();curSubTreeNodeSet.add(startNodeId);intnumDfsRecNodes=dfsRecursive(startNodeId,adjGraph,curSubTreeNodeSet,visited,dfsRadius,remainingDepth);if(vDbgL1){Stringmsg="DFS Recursive from Node >"+startNodeId+"< results in "+"numDfsRecNodes = >"+numDfsRecNodes+"< | "+"curSubTreeNodeSet.size() = >"+curSubTreeNodeSet.size()+"< | ";if(curSubTreeNodeSet.size()<=VDBG_MAX_SUBTREE_NODE_SIZE){msg+="curSubTreeNodeSet = >"+curSubTreeNodeSet+"<";}System.err.println(msg);}returncurSubTreeNodeSet;}// Depth-First-Search (DFS) - Actual Recursive MethodprivatestaticintdfsRecursive(intnode,AdjGraphadjGraph,Set<Integer>curSubTreeNodeSet,boolean[]visited,intdfsRadius,intremainingDepth){if(vDbgL2){System.err.println("DFS Recursive from Node >"+node+"< | remainingDepth = >"+remainingDepth+"<");}visited[node]=true;curSubTreeNodeSet.add(node);// Base Condition: Terminate Recursive DFS once we have explored upto radius 'r'if(remainingDepth==0){if(vDbgL4){System.err.println("DFS Recursive from Node >"+node+"< "+"is at dfsRadius = >"+dfsRadius+"< | Avoiding further DFS!");}return1;}intnumDfsRecNodes=1;for(intnei:adjGraph.getAdj().get(node)){if(!visited[nei]){numDfsRecNodes+=dfsRecursive(nei,adjGraph,curSubTreeNodeSet,visited,dfsRadius,remainingDepth-1);}}returnnumDfsRecNodes;}// Depth-First-Search (DFS) - Non-Recursive / Iterative (via Explicit Stack)privatestaticSet<Integer>dfsNonRecIter(intstartNodeId,AdjGraphadjGraph,intmaxNodeId,intdfsRadius){boolean[]visited=newboolean[maxNodeId+1];visited[startNodeId]=true;Stack<RelativeNodeDepth>dfsStack=newStack<RelativeNodeDepth>();dfsStack.push(newRelativeNodeDepth(startNodeId,startNodeId,0));intnumDfsIterNodes=0;Set<Integer>curSubTreeNodeSet=newHashSet<Integer>();curSubTreeNodeSet.add(startNodeId);while(!dfsStack.isEmpty()){if(vDbgL2){System.err.println("DFS Iterative from Node >"+startNodeId+"< | dfsStack = >"+dfsStack+"<");}RelativeNodeDepthtopStackDfsNode=dfsStack.pop();numDfsIterNodes++;if(vDbgL3){System.err.println("DFS Iterative from Node >"+startNodeId+"< | "+"topStackDfsNode (popped) = >"+topStackDfsNode+"<");}if(topStackDfsNode.depthDist<dfsRadius){for(intnei:adjGraph.getAdj().get(topStackDfsNode.curNodeId)){if(!visited[nei]){visited[nei]=true;curSubTreeNodeSet.add(nei);RelativeNodeDepthnextDfsNode=newRelativeNodeDepth(startNodeId,nei,topStackDfsNode.depthDist+1);dfsStack.push(nextDfsNode);}}}else{if(vDbgL4){System.err.println("DFS Iterative from Node >"+startNodeId+"< | "+"topStackDfsNode = >"+topStackDfsNode+"< is at "+"dfsRadius = >"+dfsRadius+"< | Avoiding further DFS!");}}}if(vDbgL1){Stringmsg="DFS Iterative from Node >"+startNodeId+"< results in "+"numDfsIterNodes = >"+numDfsIterNodes+"< | "+"curSubTreeNodeSet.size() = >"+curSubTreeNodeSet.size()+"< | ";if(curSubTreeNodeSet.size()<=VDBG_MAX_SUBTREE_NODE_SIZE){msg+="curSubTreeNodeSet = >"+curSubTreeNodeSet+"<";}System.err.println(msg);}returncurSubTreeNodeSet;}// Breadth-First-Search (DFS) - Non-Recursive / Iterative (via Explicit Queue)privatestaticSet<Integer>bfsIterative(intstartNodeId,AdjGraphadjGraph,intmaxNodeId,intbfsRadius){boolean[]visited=newboolean[maxNodeId+1];visited[startNodeId]=true;intnumBfsNodes=0;Queue<RelativeNodeDepth>bfsQ=newLinkedList<RelativeNodeDepth>();bfsQ.add(newRelativeNodeDepth(startNodeId,startNodeId,0));Set<Integer>curSubTreeNodeSet=newHashSet<Integer>();curSubTreeNodeSet.add(startNodeId);while(!bfsQ.isEmpty()){if(vDbgL2){System.err.println("BFS Iterative from Node >"+startNodeId+"< | bfsQ = >"+bfsQ+"<");}RelativeNodeDepthheadBfsQNode=bfsQ.poll();numBfsNodes++;if(vDbgL3){System.err.println("BFS Iterative from Node >"+startNodeId+"< | "+"headBfsQNode = >"+headBfsQNode+"<");}if(headBfsQNode.depthDist<bfsRadius){for(intnei:adjGraph.getAdj().get(headBfsQNode.curNodeId)){if(!visited[nei]){visited[nei]=true;curSubTreeNodeSet.add(nei);RelativeNodeDepthnextBfsNode=newRelativeNodeDepth(startNodeId,nei,headBfsQNode.depthDist+1);bfsQ.add(nextBfsNode);}}}else{if(vDbgL4){System.err.println("BFS Iterative from Node >"+startNodeId+"< | "+"headBfsQNode = >"+headBfsQNode+"< is at "+"bfsRadius = >"+bfsRadius+"< | Avoiding further BFS!");}}}if(vDbgL1){Stringmsg="BFS from Node >"+startNodeId+"< results in "+"numBfsNodes = >"+numBfsNodes+"< | "+"curSubTreeNodeSet.size() = >"+curSubTreeNodeSet.size()+"< | ";if(curSubTreeNodeSet.size()<=VDBG_MAX_SUBTREE_NODE_SIZE){msg+="curSubTreeNodeSet = >"+curSubTreeNodeSet+"<";}System.err.println(msg);}returncurSubTreeNodeSet;}// Common Regulatory Function to trigger one of the three approaches// i.e. (1)(A) DFS Recursive, (1)(B) DFS Iterative, (2) BFS IterativepublicstaticintsolveViaDfsOrBfs(intn,intr,List<List<Integer>>edges,AlgoChoicealgoChoice){StringalgoName=algoChoice.toString();// Build Adjacency GraphAdjGraphadjGraph=newAdjGraph(n,edges);// Set of Distinct SubTrees, which are NOT "isomorphic"Set<String>distinctSubTreeAdjSet=newHashSet<String>();// Set of Nodes as Key, the Canonical Hash as ValueMap<Set<Integer>,String>nodeSetCanonRepCacheMap=newHashMap<>();for(intnode=1;node<=n;node++){if(!adjGraph.getAdj().get(node).isEmpty()){Set<Integer>curSubTreeNodeSet;switch(algoChoice){caseDFS_Recursive:curSubTreeNodeSet=dfsRecWrapper(node,adjGraph,n,r,r);break;caseDFS_Iterative:curSubTreeNodeSet=dfsNonRecIter(node,adjGraph,n,r);break;caseBFS_Iterative:curSubTreeNodeSet=bfsIterative(node,adjGraph,n,r);break;default:StringerrMsg="Unknown algoChoice = >"+algoChoice+"<! Terminating!";System.err.println(errMsg);thrownewRuntimeException(errMsg);}if(curSubTreeNodeSet.size()==1){// Subtree contains single node.// We can add the Normalized Minimal Canonical Form directly.distinctSubTreeAdjSet.add(SINGLE_NODE_SUBTREE_DEFAULT_CAN_HASH_STR);continue;}if(nodeSetCanonRepCacheMap.containsKey(curSubTreeNodeSet)){// distinctSubTreeAdjSet.add(cache.get(curSubTreeNodeSet));continue;}AdjGraphcurSubTreeAdjGraph=constructSubTreeAdj(algoName,node,adjGraph,curSubTreeNodeSet);StringcurSubTreeHashedCanonicalRep=findNormMinForm(algoName,node,curSubTreeAdjGraph);distinctSubTreeAdjSet.add(curSubTreeHashedCanonicalRep);nodeSetCanonRepCacheMap.put(curSubTreeNodeSet,curSubTreeHashedCanonicalRep);}}if(vDbgL5){debugPrintFinalDistinctSubTreeAdjSet(algoName,distinctSubTreeAdjSet);}returndistinctSubTreeAdjSet.size();}/* * Complete the 'jennysSubtrees' function below. * * The function is expected to return an INTEGER. * The function accepts following parameters: * 1. INTEGER n * 2. INTEGER r * 3. 2D_INTEGER_ARRAY edges */publicstaticintjennysSubtrees(intn,intr,List<List<Integer>>edges){// Write your code hereif(r==n){return1;}returnsolveViaDfsOrBfs(n,r,edges,AlgoChoice.DFS_Recursive);// return solveViaDfsOrBfs(n, r, edges, AlgoChoice.DFS_Iterative);// return solveViaDfsOrBfs(n, r, edges, AlgoChoice.BFS_Iterative);}}
Jenny's Subtrees
You are viewing a single comment's thread. Return to all comments →
Here's my Java 8 code, which passes all test-cases, for Max Score of 50.
Couple of reasons why the code is so long: (1) Added a ton of verbose debug logging statements to trace & understand the logic. These are behind toggle switches & fine-tuned controls, (2) Implemented it via three approaches: (a) DFS Recursive, (b) DFS Iterative, (c) BFS (Iterative). And provided a quick way to toggle / cycle through all of them.