Given a tree with N nodes and N – 1 edges. Each edge of the tree is labeled by a string of lowercase english alphabets. You are given Q queries. In each query you are given two node x and y. For the path between x to y, the task is to check if it is possible to make a new palindrome string which use all the characters in the path from node x to node y. Note that you can use the characters in any order you like and root node is always 1.
Examples:
Input: 1 (bbc)/ \(ac) 2 3 Q[][] = {{1, 2}, {2, 3}, {3, 1}, {3, 3}} Output: Yes Yes No Yes Query 1: "bbc" can be arranged into "bcb" which is a palindrome. Query 2: "bbcac" -> "bcacb" Query 3: No permutation of "ac" is palindrome. Query 4: "acac" -> "acca" Input: 1 / | \ /(ab) |(bca) \(bc) 2 3 4 \(ab) 5 Q[][] = {{1, 5}, {4, 5}, {5, 4}} Output: Yes No No
Approach: For each query, the character count for path x to y has to be calculated. After finding the characters count, it can be easily checked whether the characters will form a palindrome or not using the approach discussed in this article.
Steps to get the character count for path x to y.
- Set the initial character count for each node.
- Update character count of the root’s child node and then repeat the same process for its child node.
- Find LCA (Lowest Common Ancestor) of x and y.
- Calculate character count for path x to y with the following step:
For each character charCount[i] = (xNodeCharCount[i] – lcaNodeCharCount[i]) + (yNodeCharCount[i] – lcaNodeCharCount[i])
- Now check whether a palindrome can be formed with the current count of characters.
Below is the implementation of the above approach:
C++
// C++ implementation of the approach #include <bits/stdc++.h> using namespace std; const int MAX_SIZE = 100005, MAX_CHAR = 26; int nodeCharactersCount[MAX_SIZE][MAX_CHAR]; vector< int > tree[MAX_SIZE]; // Function that returns true if a palindromic // string can be formed using the given characters bool canFormPalindrome( int * charArray) { // Count odd occurring characters int oddCount = 0; for ( int i = 0; i < MAX_CHAR; i++) { if (charArray[i] % 2 == 1) oddCount++; } // Return false if odd count is more than 1, if (oddCount >= 2) return false ; else return true ; } // Find to find the Lowest Common // Ancestor in the tree int LCA( int currentNode, int x, int y) { // Base case if (currentNode == x) return x; // Base case if (currentNode == y) return y; int xLca, yLca; // Initially value -1 denotes that // we need to find the ancestor xLca = yLca = -1; // -1 denotes that we need to find the lca // for x and y, values other than x and y // will be the LCA of x and y int gotLca = -1; // Iterating in the child // subtree of the currentNode for ( int l = 0; l < tree[currentNode].size(); l++) { // Next node that will be checked int nextNode = tree[currentNode][l]; // Look for the next child subtree int out_ = LCA(nextNode, x, y); if (out_ == x) xLca = out_; if (out_ == y) yLca = out_; // Both the nodes exist in the different // subtrees, in this case parent node // will be the lca if (xLca != -1 and yLca != -1) return currentNode; // Handle the cases where LCA is already // calculated or both x and y are present // in the same subtree if (out_ != -1) gotLca = out_; } return gotLca; } // Function to calculate the character count for // each path from node i to 1 (root node) void buildTree( int i) { for ( int l = 0; l < tree[i].size(); l++) { int nextNode = tree[i][l]; for ( int j = 0; j < MAX_CHAR; j++) { // Updating the character count // for each node nodeCharactersCount[nextNode][j] += nodeCharactersCount[i][j]; } // Look for the child subtree buildTree(nextNode); } } // Function that returns true if a palindromic path // is possible between the nodes x and y bool canFormPalindromicPath( int x, int y) { int lcaNode; // If both x and y are same then // lca will be the node itself if (x == y) lcaNode = x; // Find the lca of x and y else lcaNode = LCA(1, x, y); int charactersCountFromXtoY[MAX_CHAR] = { 0 }; // Calculating the character count // for path node x to y for ( int i = 0; i < MAX_CHAR; i++) { charactersCountFromXtoY[i] = nodeCharactersCount[x][i] + nodeCharactersCount[y][i] - 2 * nodeCharactersCount[lcaNode][i]; } // Checking if we can form the palindrome // string with the all character count if (canFormPalindrome(charactersCountFromXtoY)) return true ; return false ; } // Function to update character count at node v void updateNodeCharactersCount(string str, int v) { // Updating the character count at node v for ( int i = 0; i < str.length(); i++) nodeCharactersCount[v][str[i] - 'a' ]++; } // Function to perform the queries void performQueries(vector<vector< int > > queries, int q) { int i = 0; while (i < q) { int x = queries[i][0]; int y = queries[i][1]; // If path can be a palindrome if (canFormPalindromicPath(x, y)) cout << "Yes\n" ; else cout << "No\n" ; i++; } } // Driver code int main() { // Fill the complete array with 0 memset (nodeCharactersCount, 0, sizeof (nodeCharactersCount)); // Edge between 1 and 2 labelled "bbc" tree[1].push_back(2); updateNodeCharactersCount( "bbc" , 2); // Edge between 1 and 3 labelled "ac" tree[1].push_back(3); updateNodeCharactersCount( "ac" , 3); // Update the character count // from root to the ith node buildTree(1); vector<vector< int > > queries{ { 1, 2 }, { 2, 3 }, { 3, 1 }, { 3, 3 } }; int q = queries.size(); // Perform the queries performQueries(queries, q); return 0; } |
Java
// Java implementation of the approach import java.util.*; @SuppressWarnings ( "unchecked" ) class GFG { static int MAX_SIZE = 100005 , MAX_CHAR = 26 ; static int [][] nodeCharactersCount = new int [MAX_SIZE][MAX_CHAR]; static Vector<Integer>[] tree = new Vector[MAX_SIZE]; // Function that returns true if a palindromic // string can be formed using the given characters static boolean canFormPalindrome( int [] charArray) { // Count odd occurring characters int oddCount = 0 ; for ( int i = 0 ; i < MAX_CHAR; i++) { if (charArray[i] % 2 == 1 ) oddCount++; } // Return false if odd count is more than 1, if (oddCount >= 2 ) return false ; else return true ; } // Find to find the Lowest Common // Ancestor in the tree static int LCA( int currentNode, int x, int y) { // Base case if (currentNode == x) return x; // Base case if (currentNode == y) return y; int xLca, yLca; // Initially value -1 denotes that // we need to find the ancestor xLca = yLca = - 1 ; // -1 denotes that we need to find the lca // for x and y, values other than x and y // will be the LCA of x and y int gotLca = - 1 ; // Iterating in the child // subtree of the currentNode for ( int l = 0 ; l < tree[currentNode].size(); l++) { // Next node that will be checked int nextNode = tree[currentNode].elementAt(l); // Look for the next child subtree int out_ = LCA(nextNode, x, y); if (out_ == x) xLca = out_; if (out_ == y) yLca = out_; // Both the nodes exist in the different // subtrees, in this case parent node // will be the lca if (xLca != - 1 && yLca != - 1 ) return currentNode; // Handle the cases where LCA is already // calculated or both x and y are present // in the same subtree if (out_ != - 1 ) gotLca = out_; } return gotLca; } // Function to calculate the character count for // each path from node i to 1 (root node) static void buildTree( int i) { for ( int l = 0 ; l < tree[i].size(); l++) { int nextNode = tree[i].elementAt(l); for ( int j = 0 ; j < MAX_CHAR; j++) { // Updating the character count // for each node nodeCharactersCount[nextNode][j] += nodeCharactersCount[i][j]; } // Look for the child subtree buildTree(nextNode); } } // Function that returns true if a palindromic path // is possible between the nodes x and y static boolean canFormPalindromicPath( int x, int y) { int lcaNode; // If both x and y are same then // lca will be the node itself if (x == y) lcaNode = x; // Find the lca of x and y else lcaNode = LCA( 1 , x, y); int [] charactersCountFromXtoY = new int [MAX_CHAR]; Arrays.fill(charactersCountFromXtoY, 0 ); // Calculating the character count // for path node x to y for ( int i = 0 ; i < MAX_CHAR; i++) { charactersCountFromXtoY[i] = nodeCharactersCount[x][i] + nodeCharactersCount[y][i] - 2 * nodeCharactersCount[lcaNode][i]; } // Checking if we can form the palindrome // string with the all character count if (canFormPalindrome(charactersCountFromXtoY)) return true ; return false ; } // Function to update character count at node v static void updateNodeCharactersCount(String str, int v) { // Updating the character count at node v for ( int i = 0 ; i < str.length(); i++) nodeCharactersCount[v][str.charAt(i) - 'a' ]++; } // Function to perform the queries static void performQueries( int [][] queries, int q) { int i = 0 ; while (i < q) { int x = queries[i][ 0 ]; int y = queries[i][ 1 ]; // If path can be a palindrome if (canFormPalindromicPath(x, y)) System.out.println( "Yes" ); else System.out.println( "No" ); i++; } } // Driver Code public static void main(String[] args) { // Fill the complete array with 0 for ( int i = 0 ; i < MAX_SIZE; i++) { for ( int j = 0 ; j < MAX_CHAR; j++) { nodeCharactersCount[i][j] = 0 ; } } for ( int i = 0 ; i < MAX_SIZE; i++) { tree[i] = new Vector<>(); } // Edge between 1 and 2 labelled "bbc" tree[ 1 ].add( 2 ); updateNodeCharactersCount( "bbc" , 2 ); // Edge between 1 and 3 labelled "ac" tree[ 1 ].add( 3 ); updateNodeCharactersCount( "ac" , 3 ); // Update the character count // from root to the ith node buildTree( 1 ); int [][] queries = { { 1 , 2 }, { 2 , 3 }, { 3 , 1 }, { 3 , 3 } }; int q = queries.length; // Perform the queries performQueries(queries, q); } } // This code is contributed by // sanjeev2552 |
C#
// C# implementation of the approach using System; using System.Collections.Generic; class GFG { static int MAX_SIZE = 100005, MAX_CHAR = 26; static int [,] nodecharsCount = new int [MAX_SIZE, MAX_CHAR]; static List< int >[] tree = new List< int >[MAX_SIZE]; // Function that returns true if a palindromic // string can be formed using the given characters static bool canFormPalindrome( int [] charArray) { // Count odd occurring characters int oddCount = 0; for ( int i = 0; i < MAX_CHAR; i++) { if (charArray[i] % 2 == 1) oddCount++; } // Return false if odd count is more than 1, if (oddCount >= 2) return false ; else return true ; } // Find to find the Lowest Common // Ancestor in the tree static int LCA( int currentNode, int x, int y) { // Base case if (currentNode == x) return x; // Base case if (currentNode == y) return y; int xLca, yLca; // Initially value -1 denotes that // we need to find the ancestor xLca = yLca = -1; // -1 denotes that we need to find the lca // for x and y, values other than x and y // will be the LCA of x and y int gotLca = -1; // Iterating in the child // subtree of the currentNode for ( int l = 0; l < tree[currentNode].Count; l++) { // Next node that will be checked int nextNode = tree[currentNode][l]; // Look for the next child subtree int out_ = LCA(nextNode, x, y); if (out_ == x) xLca = out_; if (out_ == y) yLca = out_; // Both the nodes exist in the different // subtrees, in this case parent node // will be the lca if (xLca != -1 && yLca != -1) return currentNode; // Handle the cases where LCA is already // calculated or both x and y are present // in the same subtree if (out_ != -1) gotLca = out_; } return gotLca; } // Function to calculate the character count for // each path from node i to 1 (root node) static void buildTree( int i) { for ( int l = 0; l < tree[i].Count; l++) { int nextNode = tree[i][l]; for ( int j = 0; j < MAX_CHAR; j++) { // Updating the character count // for each node nodecharsCount[nextNode,j] += nodecharsCount[i,j]; } // Look for the child subtree buildTree(nextNode); } } // Function that returns true if a palindromic path // is possible between the nodes x and y static bool canFormPalindromicPath( int x, int y) { int lcaNode; // If both x and y are same then // lca will be the node itself if (x == y) lcaNode = x; // Find the lca of x and y else lcaNode = LCA(1, x, y); int [] charactersCountFromXtoY = new int [MAX_CHAR]; // Calculating the character count // for path node x to y for ( int i = 0; i < MAX_CHAR; i++) { charactersCountFromXtoY[i] = nodecharsCount[x, i] + nodecharsCount[y, i] - 2 * nodecharsCount[lcaNode, i]; } // Checking if we can form the palindrome // string with the all character count if (canFormPalindrome(charactersCountFromXtoY)) return true ; return false ; } // Function to update character count at node v static void updateNodecharsCount(String str, int v) { // Updating the character count at node v for ( int i = 0; i < str.Length; i++) nodecharsCount[v, str[i] - 'a' ]++; } // Function to perform the queries static void performQueries( int [,] queries, int q) { int i = 0; while (i < q) { int x = queries[i, 0]; int y = queries[i, 1]; // If path can be a palindrome if (canFormPalindromicPath(x, y)) Console.WriteLine( "Yes" ); else Console.WriteLine( "No" ); i++; } } // Driver Code public static void Main(String[] args) { // Fill the complete array with 0 for ( int i = 0; i < MAX_SIZE; i++) { for ( int j = 0; j < MAX_CHAR; j++) { nodecharsCount[i, j] = 0; } } for ( int i = 0; i < MAX_SIZE; i++) { tree[i] = new List< int >(); } // Edge between 1 and 2 labelled "bbc" tree[1].Add(2); updateNodecharsCount( "bbc" , 2); // Edge between 1 and 3 labelled "ac" tree[1].Add(3); updateNodecharsCount( "ac" , 3); // Update the character count // from root to the ith node buildTree(1); int [,] queries = { { 1, 2 }, { 2, 3 }, { 3, 1 }, { 3, 3 } }; int q = queries.GetLength(0); // Perform the queries performQueries(queries, q); } } // This code is contributed by aashish1995 |
Javascript
<script> // JavaScript implementation of the approach let MAX_SIZE = 100005, MAX_CHAR = 26; let nodeCharactersCount = new Array(MAX_SIZE); let tree = new Array(MAX_SIZE); // Function that returns true if a palindromic // string can be formed using the given characters function canFormPalindrome(charArray) { // Count odd occurring characters let oddCount = 0; for (let i = 0; i < MAX_CHAR; i++) { if (charArray[i] % 2 == 1) oddCount++; } // Return false if odd count is more than 1, if (oddCount >= 2) return false ; else return true ; } // Find to find the Lowest Common // Ancestor in the tree function LCA(currentNode, x, y) { // Base case if (currentNode == x) return x; // Base case if (currentNode == y) return y; let xLca, yLca; // Initially value -1 denotes that // we need to find the ancestor xLca = yLca = -1; // -1 denotes that we need to find the lca // for x and y, values other than x and y // will be the LCA of x and y let gotLca = -1; // Iterating in the child // subtree of the currentNode for (let l = 0; l < tree[currentNode].length; l++) { // Next node that will be checked let nextNode = tree[currentNode][l]; // Look for the next child subtree let out_ = LCA(nextNode, x, y); if (out_ == x) xLca = out_; if (out_ == y) yLca = out_; // Both the nodes exist in the different // subtrees, in this case parent node // will be the lca if (xLca != -1 && yLca != -1) return currentNode; // Handle the cases where LCA is already // calculated or both x and y are present // in the same subtree if (out_ != -1) gotLca = out_; } return gotLca; } // Function to calculate the character count for // each path from node i to 1 (root node) function buildTree(i) { for (let l = 0; l < tree[i].length; l++) { let nextNode = tree[i][l]; for (let j = 0; j < MAX_CHAR; j++) { // Updating the character count // for each node nodeCharactersCount[nextNode][j] += nodeCharactersCount[i][j]; } // Look for the child subtree buildTree(nextNode); } } // Function that returns true if a palindromic path // is possible between the nodes x and y function canFormPalindromicPath(x, y) { let lcaNode; // If both x and y are same then // lca will be the node itself if (x == y) lcaNode = x; // Find the lca of x and y else lcaNode = LCA(1, x, y); let charactersCountFromXtoY = new Array(MAX_CHAR); charactersCountFromXtoY.fill(0); // Calculating the character count // for path node x to y for (let i = 0; i < MAX_CHAR; i++) { charactersCountFromXtoY[i] = nodeCharactersCount[x][i] + nodeCharactersCount[y][i] - 2 * nodeCharactersCount[lcaNode][i]; } // Checking if we can form the palindrome // string with the all character count if (canFormPalindrome(charactersCountFromXtoY)) return true ; return false ; } // Function to update character count at node v function updateNodeCharactersCount(str, v) { // Updating the character count at node v for (let i = 0; i < str.length; i++) nodeCharactersCount[v][str[i].charCodeAt() - 'a' .charCodeAt()]++; } // Function to perform the queries function performQueries(queries, q) { let i = 0; while (i < q) { let x = queries[i][0]; let y = queries[i][1]; // If path can be a palindrome if (canFormPalindromicPath(x, y)) document.write( "Yes" + "</br>" ); else document.write( "No" + "</br>" ); i++; } } // Fill the complete array with 0 for (let i = 0; i < MAX_SIZE; i++) { nodeCharactersCount[i] = new Array(MAX_CHAR); for (let j = 0; j < MAX_CHAR; j++) { nodeCharactersCount[i][j] = 0; } } for (let i = 0; i < MAX_SIZE; i++) { tree[i] = []; } // Edge between 1 and 2 labelled "bbc" tree[1].push(2); updateNodeCharactersCount( "bbc" , 2); // Edge between 1 and 3 labelled "ac" tree[1].push(3); updateNodeCharactersCount( "ac" , 3); // Update the character count // from root to the ith node buildTree(1); let queries = [ [ 1, 2 ], [ 2, 3 ], [ 3, 1 ], [ 3, 3 ] ]; let q = queries.length; // Perform the queries performQueries(queries, q); </script> |
Javascript
// JavaScript implementation of the approach let MAX_SIZE = 1000; let tree = new Array(MAX_SIZE); for (let i = 0; i < tree.length; i++) { tree[i] = []; } function addEdgesWithLabel(a, b, str) { tree[a].push([b, str]); tree[b].push([a, str]); } function dfs(node, parent, label, lev) { memo[node][0] = parent; level[node] = lev; label_array[node][0] = label; for (let i = 1; i <= log; i++) { if (memo[node][i - 1] != -1) { memo[node][i] = memo[memo[node][i - 1]][i - 1]; label_array[node][i] = label_array[node][i] + label_array[memo[node][i - 1]][i - 1]; } } for (let i = 0; i < tree[node].length; i++) { if (tree[node][i][0] != parent) { dfs(tree[node][i][0], node, tree[node][i][1], level[node] + 1) } } } function findPathString(u, v) { let res = '' ; if (level[u] < level[v]) { let temp = u; u = v; v = temp; } for (let i = log; i >= 0; i--) { if (level[u] - Math.pow(2, i) >= level[v]) { res = res + label_array[u][i]; u = memo[u][i]; } } if (u == v) { return res; } for (let i = log; i >= 0; i--) { if (memo[u][i] != memo[v][i]) { res = res + label_array[u][i] + label_array[v][i]; v = memo[v][i]; u = memo[u][i]; } } res = res + label_array[u][0] + label_array[v][0]; return res; } function checkIfPalindrom(str) { // Create a list let list = []; // For each character in input strings, // remove character if list contains // else add character to list for (let i = 0; i < str.length; i++) { if (list.includes(str[i])) list.splice(list.indexOf(str[i]), 1); else list.push(str[i]); } // If character length is even // list is expected to be empty or // if character length is odd list size // is expected to be 1 // If string length is even if (str.length % 2 == 0 && list.length == 0 || (str.length % 2 == 1 && list.length == 1)) return 'YES' ; // If string length is odd else return 'NO' ; } let memo = new Array(MAX_SIZE); let log = Math.ceil(Math.log(MAX_SIZE) / Math.log(2)); for (let i = 0; i < memo.length; i++) { memo[i] = new Array(log + 1); for (let j = 0; j < log + 1; j++) { memo[i][j] = -1; } } let label_array = new Array(MAX_SIZE); for (let i = 0; i < label_array.length; i++) { label_array[i] = new Array(log + 1); for (let j = 0; j < log + 1; j++) { label_array[i][j] = '' ; } } let level = new Array(MAX_SIZE); level.fill(0); addEdgesWithLabel(1, 2, 'bbc' ); addEdgesWithLabel(1, 3, 'ac' ); dfs(1, -1, '' , 0); document.write(checkIfPalindrom(findPathString(1, 2)) + '<br>' ); document.write(checkIfPalindrom(findPathString(2, 3)) + '<br>' ); document.write(checkIfPalindrom(findPathString(3, 1)) + '<br>' ); document.write(checkIfPalindrom(findPathString(3, 3)) + '<br>' ); // This code is contributed by gaurav2146 |
Yes Yes No Yes
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!