[Solved] K-th Symbol in Grammar Contest Problem

K-th Symbol in Grammar: You are given two integers, ‘N’ and ‘K’. Your task is to return the ‘K-th’ bit in row ‘N’ of the given sequence.

Sequence: The first row contains a single bit ‘1’. In each subsequent row, we look at the previous row and replace each ‘1’ with ‘10’ and each ‘0’ with ‘01’.

K-th Symbol in Grammar Contest Problem

Example:

N = 4, K = 5

Given sequence:
The previous row’s ‘1’ becomes ‘10’, and ‘0’ becomes ‘01’ for each subsequent row. The contents of each row are:
Row 1: 1
As, ‘1’ => ‘10’, So ‘1’ (row 1) => ‘10’ (row 2)

Row 2: 10
As ‘1’ => ‘10’& ‘0’ => ‘01’,So ‘10’ (row 2) => ‘1001’ (row 3)

Row 3: 1001
As ‘10’ => ‘1001’ & ‘01’ => ‘0110’, So ‘1001’ (row 3) => ‘10010110’ (row 4)

Row 4: 10010110
The ‘5-th’ bit in row ‘4’ is ‘0’. Return ‘0’ as the answer.

Note:

Both ‘K’ and ‘N’ have 1-based indexing.

Input Format:

The first line of input contains an integer ‘T’ which denotes the number of test cases. Then, the ‘T’ test cases follow.

The first and only line of each test case contains two space-separated integers, ‘N’ and ‘K’, denoting the row number and index of the bit you need to return.

Output Format:

For every test case, print a single line containing a single integer (0 or 1) representing the ‘K-th’ bit in the row ‘N’.

Note:

You do not need to input or print anything, as it has already been taken care of. Just implement the given function.

Constraints:

1 <= T <= 10 ^ 4
1 <= N <= 30
1 <= K <= 2 ^ (N - 1)

Where ‘T’ is the number of test cases, ‘N’ is the row number, and ‘K’ is the bit index.

Time limit: 1 second.

Sample input 1:

2
3 4
5 8

Sample output 1:

1
0

Explanation of sample input 1:

Test Case 1:
N = 3, K = 4

Given sequence:
Row 1: 1
Row 2: 10
Row 3: 1001
The ‘4-th’ bit in row ‘3’ is 1. Return ‘1’ as the answer.

Test Case 2:
N = 5, K = 8

Given sequence:
Row 1: 1
Row 2: 10
Row 3: 1001
Row 4: 10010110
Row 5: 1001011001101001
The ‘8-th’ bit in row ‘5’ is ‘0’. Return ‘0’ as the answer.

Sample input 2:

2
3 2
2 1

Sample output 2:

0
1

Solution

Recursive approach

We can view the given sequence as a binary tree with the root node as ‘1’. When a node is ‘1’, it has two children nodes as ‘1’ and ‘0’, and when it is ‘0’, the two children nodes are ‘0’ and ‘1’. If ‘k’ is odd, then the current node is the first child, else it’s the second child. The first child node has the same value as the parent node, and the second child node has the opposite value. The current node’s value depends on the parent node, so we need a recursive function that keeps going to the parent node until we reach the root node/ first row (whose node value is ‘1’). Another observation, the first row is always a single ‘1’, so the first bit of each row’s subsequent rows will also be ‘1’. Hence, we don’t always need to go to the first row. Instead, we check if the parent node stores the first bit in that row (the first bit is ‘1’). We call the given ‘kthValue’ function recursively to get the value stored at ‘k-th’ bit.

  • If ‘k’ is equal to ‘1’, return ‘1’.
  • If ‘k’ is odd, then:
    • ‘parent = kthValue(n – 1, (k+1)/2)’.
  • If ‘k’ is even, then:
    • ‘parent = kthValue(n – 1, k/2)’.
    • ‘parent = parent XOR 1’. As ‘k’ is even the current node stores the opposite value as ‘parent’/the previous row. Here, ‘XOR’ means bitwise xor.
  • Return ‘parent’ as the answer.

Time Complexity

O(log(K)), where ‘K’ is the bit index.

To find the bit at index ‘K’, we first find the value stored in its parent at index ‘K / 2’ and so on recursively until we reach index ‘1’, making ‘log(K)’ function calls.

Space Complexity

O(log(K)), where ‘K’ is the bit index.

We make ‘log(K)’ recursive calls. So, the size of the call stack used for recursion is ‘log(K)’.

/*
    Time Complexity : O(log(K))
    Space Complexity : O(log(K))

    where K is the bit index.
*/

public class Solution {
	public static int kthValue(int n, int k) {
        if (k == 1) 
        {  // The first bit of all rows is '1'.
            return 1;
        }

        int parent;
        if (k %2 == 1) {
            // Odd nodes store the same value as parent node.
            parent = kthValue(n - 1, (k + 1) / 2);  
        } else {
            parent = kthValue(n - 1, k / 2);
            // Even nodes store the opposte value as parent node.
            parent = parent ^ 1;  
        }
        return parent;
	}
}
/*
    Time Complexity : O(log(K))
    Space Complexity : O(log(K))

    where 'K' is the bit index.
*/

int kthValue(int n, int k) 
{
    if (k == 1) 
	{  // The first bit of all rows is '1'.
        return 1;
    }

    int parent;
    if (k & 1) 
	{
		// Odd nodes store the same value as parent node.
        parent = kthValue(n - 1, (k + 1) / 2);  
    } 
	else 
	{
        parent = kthValue(n - 1, k / 2);
		// Even nodes store the opposte value as parent node.
        parent = parent ^ 1;  
    }
    return parent;
}
'''
    Time Complexity : O(log(K))
    Space Complexity : O(log(K))

    where ‘k’ is the bit index.
'''

def kthValue( n,  k):
    
    # The first bit of all rows is '1'.
    if (k == 1):
        return 1

    # Odd nodes store the same value as parent node.
    if (k & 1):
        parent = kthValue(n - 1, (k + 1) // 2)
    
    # Even nodes store the opposte value as parent node.
    else:
        parent = kthValue(n - 1, k // 2)
        parent = parent ^ 1

    return parent
Iterative approach

In the previous approach, we stop the recursion when ‘k’ becomes ‘1’ as we know the value stored in the first index of each row is ‘1’. We only flip the value stored at the parent node when ‘k’ is even. Store ‘1’ as the answer. Run a loop where we update ‘k’ with its parent’s index until ‘k’ becomes ‘1’, and in each iteration, if ‘k’ is even, then flip the bit stored in answer.

  • Initialize an integer ‘answer’ to ‘1’. Use it to store the answer.
  • Run a loop until ‘k’ is not equal to ‘1’:
    • If ‘k’ is odd, then:
      • ‘k = (k + 1) / 2’
    • If ‘k’ is even, then:
      • ‘k /= 2’
      • ‘answer = answer XOR 1’. Flip the bit stored in ‘answer’. Here, ‘XOR’ means bitwise xor.
  • Return ‘answer’ as the answer.

Time Complexity

O(log(K)), where ‘K’ is the bit index.

In each iteration, we update ‘K’ with ‘K / 2’ and stop when ‘k’ becomes ‘1’. Hence the number of iterations is ‘log(K)’.

Space Complexity

O(1).

Since we are not using any extra space, space complexity is constant.

/*
    Time Complexity : O(log(K))
    Space Complexity : O(log(K))

    where K is the bit index.
*/

public class Solution {
	public static int kthValue(int n, int k) {
        if (k == 1) 
        {  // The first bit of all rows is '1'.
            return 1;
        }

        int parent;
        if (k %2 == 1) {
            // Odd nodes store the same value as parent node.
            parent = kthValue(n - 1, (k + 1) / 2);  
        } else {
            parent = kthValue(n - 1, k / 2);
            // Even nodes store the opposte value as parent node.
            parent = parent ^ 1;  
        }
        return parent;
	}
}
/*
    Time Complexity : O(log(K))
    Space Complexity : O(1)

    where 'K' is the bit index.
*/

int kthValue(int n, int k) {
    int answer = 1;

    while (k != 1) {
        if (k & 1) {
			// Odd nodes store the same value as parent node.
            k = (k + 1) / 2;  
        } else {
            k = k / 2;
			// Even nodes store the opposte value as parent node.
            answer = answer ^ 1;  
        }
    }
    return answer;
}
'''
    Time Complexity : O(log(K))
    Space Complexity : O(1)

    where ‘k’ is the bit index. 
'''

def kthValue(n,  k):

    answer = 1

    while (k != 1):

        # Odd nodes store the same value as parent node.
        if (k & 1):
            k = (k + 1) // 2

        # Even nodes store the opposte value as parent node.
        else:
            k = k // 2
            answer = answer ^ 1

    return answer
Count set bits in ‘K – 1’

An alternate implementation for the iterative approach would be to count the set bits in the binary representation of ‘k-1’. If ‘k’ has 0-based indexing, then the parent of ‘k’ will always have its index as ‘k/2’. Now we toggle the parent bit if ‘k’ is odd,i.e., the Least Significant Bit (LSD) bit of ‘k’ is ‘1’. And ‘k/2’ is similar to a left shift operation on the binary form of ‘k’.

For example: ‘k’ = ‘11’

‘k-1’ = 10 = (1010)

Numbers in () are binary, and ‘>>’ is the left shift operator.

10/2 = 5 = (1010) >> 1 = (101) LSD is 1 so we need to toggle the parent bit.

5/2 = 2 = (101) >> 1 = (10) No need to togglethe parent bit.

2/2 = 1 = (10) >> 1 = (1) LSD is 1 so we need to toggle the parent bit.

So, the number of times we need to toggle the parent bit is equal to ‘2’, i.e., the count of set bits in ‘k-1’.

The result of toggling a bit ‘x’ odd number of times is equivalent to toggling ‘x’ once, and for an even number of times, the result is ‘x’. If we need to toggle the ‘k-th’ bit an even number of times, it’s equal to the tree’s root,i.e., ‘1’. Else the ‘k-th’ bit is ‘0’. To count the number of bits in ‘k-1’ efficiently, we can use  https://sanchit3b.medium.com/brian-kernighans-algorithm-9e0ca5989148  or some inbuilt function like:

C++:      __builtin_popcount 

Python: bin(number).count(‘1’)

  • Initialize an integer ‘bitCount’ to 0. Use this to count the number of bits in ‘k-1’.
  • ‘k -= 1’
  • Run a loop until ‘k’ is not equal to ‘0’ (Brian Kernighan’s algorithm for bit count):
    • ‘bitCount += 1’
    • ‘k = k AND (k – 1)’. Here, ‘AND’ means bitwise and.
  • If ‘bitCount’ is odd, return ‘0’ as the answer.
  • Else, return ‘1’ as the answer.

Time Complexity

O(Number of bits in (K – 1)), where ‘K’ is the bit index.

The time required to count the number of bits in integer ‘N’ by Brian Kernighan Algorithm is equal to ‘O(Number of bits in N)’. 

Space Complexity

O(1).

Since we are not using any extra space, space complexity is constant.

/*
    Time Complexity : O(log(K))
    Space Complexity : O(log(K))

    where K is the bit index.
*/

public class Solution {
	public static int kthValue(int n, int k) {
        if (k == 1) 
        {  // The first bit of all rows is '1'.
            return 1;
        }

        int parent;
        if (k %2 == 1) {
            // Odd nodes store the same value as parent node.
            parent = kthValue(n - 1, (k + 1) / 2);  
        } else {
            parent = kthValue(n - 1, k / 2);
            // Even nodes store the opposte value as parent node.
            parent = parent ^ 1;  
        }
        return parent;
	}
}
/*
    Time Complexity : O(Number of bits in (K-1))
    Space Complexity : O(1)

    where 'K' is the bit index.
*/

int kthValue(int n, int k) 
{
    int bitCount = 0;
    k--;

    // Count the set bits in 'k-1'.
    while (k) 
	{
        bitCount++;
        k = k & (k - 1);
    }

    if (bitCount & 1) 
	{
        return 0;
    } 
	else 
	{
        return 1;
    }
}
'''
    Time Complexity : O(Number of bits in (K-1))
    Space Complexity : O(1)

    where ‘k’ is the bit index.  
'''

def kthValue(n,  k):

    bitCount = 0
    k -= 1

    # Count the set bits in 'k-1'.
    while (k):
        bitCount += 1
        k = k & (k - 1)

    if (bitCount & 1):
        return 0

    else:
        return 1

Happy Learning – If you require any further information, feel free to contact me.

Share your love
Saurav Hathi

Saurav Hathi

I'm currently studying Bachelor of Computer Science at Lovely Professional University in Punjab.

📌 Nodejs and Android 😎
📌 Java

Articles: 444

Leave a Reply

Your email address will not be published. Required fields are marked *