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++ 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 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# 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 |
<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 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