Queries to check if the path between two nodes in a tree is a palindrome

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.

  1. Set the initial character count for each node.
  2. Update character count of the root’s child node and then repeat the same process for its child node.
  3. Find LCA (Lowest Common Ancestor) of x and y.
  4. 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])
  5. Now check whether a palindrome can be formed with the current count of characters.

Below is the implementation of the above approach:

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// 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
    // substree 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;
}

chevron_right


Java

filter_none

edit
close

play_arrow

link
brightness_4
code

// 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
        // substree 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

chevron_right


Output:

Yes
Yes
No
Yes

Don’t stop now and take your learning to the next level. Learn all the important concepts of Data Structures and Algorithms with the help of the most trusted course: DSA Self Paced. Become industry ready at a student-friendly price.




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.



Improved By : sanjeev2552