Open In App

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

Output: 
Yes
Yes
No
Yes

 


Article Tags :