Count the subtree - optimise - python-3.x

COUNT THE SUBTREES
Given a tree having N nodes numbered from 1 to N and (N-1) edges where the ith edge is between node Edges [i][0] and node Edges[i][1].
The tree is rooted at node 1 and each node has a colour where the colour of the ith node is given by A[i].
Count the number of subtrees that do not contain any two nodes which have the same color.
Example:
Input:
N = 4
A[] = {1, 1, 2, 3}
Edges[][] = {{1, 2},
{2, 3},
{2, 4}}
Output:
3
Explanation:
The structure of the tree is:
1
|
2
/ \
3 4
There are only three subtrees rooted at node 2, 3 and 4 which do not contain any two
nodes of the same color.
The subtree rooted at node 1 contains two nodes which have the same color
(i.e. node 1 and node 2)
Input:
N = 2
A[] = {5, 2}
Edges[][] = {{2, 1}}
Output:
2
Constraints:
1 <= N <= 10^5
1 <= A[i] <= 10^9
1 <= Edges[i][0], Edges[i][1] <= N
Code:
def countSubtree(self, N, A, edges):
adj = defaultdict(list)
for i in edges:
adj[i[0]].append(i[1])
adj[i[1]].append(i[0])
visited = [0 for i in range(N)]
subtrees = 0
def dfs(node):
sofar = True
colors = set([A[node-1]])
visited[node-1] = 1
for i in adj[node]:
if visited[i-1] == 1:
continue
check,temp = dfs(i)
sofar = sofar and check
if colors&temp:
sofar = False
colors = colors|temp
nonlocal subtrees
if sofar:
subtrees+=1
visited[node-1] = 0
return [sofar,colors]
dfs(1)
return subtrees
The above code resulted in TLE. Is there any better approach to do this? Or can it be optimised?

Related

Why shortest path not always found for Leetcode 1129 challenge?

I am trying to solve LeetCode problem 1129. Shortest Path with Alternating Colors:
You are given an integer n, the number of nodes in a directed graph where the nodes are labeled from 0 to n - 1. Each edge is red or blue in this graph, and there could be self-edges and parallel edges.
You are given two arrays redEdges and blueEdges where:
redEdges[i] = [aᵢ, bᵢ] indicates that there is a directed red edge from node aᵢ to node bᵢ in the graph, and
blueEdges[j] = [uⱼ, vⱼ] indicates that there is a directed blue edge from node uⱼ to node vⱼ in the graph.
Return an array answer of length n, where each answer[x] is the length of the shortest path from node 0 to node x such that the edge colors alternate along the path, or -1 if such a path does not exist.
Example 1:
Input: n = 3, redEdges = [[0,1],[1,2]], blueEdges = []
Output: [0,1,-1]
Here is my code for that problem:
class Solution:
def shortestAlternatingPaths(self, n: int, redEdges: List[List[int]], blueEdges: List[List[int]]) -> List[int]:
res = [0] + [-1]*(n-1)
Red = defaultdict(list)
Blue = defaultdict(list)
for x,y in redEdges:
if x!=0 or y!=0:
Red[x].append(y)
for x,y in blueEdges:
if x!=0 or y!=0:
Blue[x].append(y)
def dfs(vertex,color,cost):
if color == "red":
for x in Red[vertex]:
if res[x] != -1:
res[x] = min(cost,res[x])
else:
res[x] = cost
if vertex in Red.keys():
del Red[vertex]
dfs(x,"blue",cost+1)
else:
for x in Blue[vertex]:
if res[x] != -1:
res[x] = min(cost,res[x])
else:
res[x] = cost
if vertex in Blue.keys():
del Blue[vertex]
dfs(x,"red",cost+1)
dfs(0,"red",1)
dfs(0,"blue",1)
return res
But for the following input:
redEdges=[[2,2],[0,1],[0,3],[0,0],[0,4],[2,1],[2,0],[1,4],[3,4]]
blueEdges=[[1,3],[0,0],[0,3],[4,2],[1,0]]
...my output is:
[0,1,4,1,1]
But the correct solution is:
[0,1,2,1,1]
...because there is a path from node 0 to node 2 like this:
red blue
0 -----> 4 -----> 2
I have no idea why my code doesn't give 2 as this path should be found via my DFS algorithm.
I thought that it might be something with the [0,0] edge, but it seems that it doesn't have an impact on a solution.
What is wrong in my code?
The problem is that your code deletes the vertex it has visited, but doesn't restore it when backtracking. There is a possibility that there is another path from vertex 0 to the one you just deleted that still needs to be traversed and is a shorter path.
Here is example input that demonstrates the problem:
redEdges = [[0,1],[2,3],[0,3]]
blueEdges = [[1,2],[3,4]]
Your code will correctly create the following adjacency lists:
Red = {0: [1, 3], 2: [3]}
Blue = {1: [2], 3: [4]}
With dfs the path 0, 1, 2, 3, 4 will be visited and during this traversal all the keys in these dictionaries will be deleted. As the loop over the outgoing edges from 0 is still active, dfs will still follow the red edge from 0 to 3, but there it finds no blue edges as the key 3 is not there anymore. And so the algorithm doesn't see the shorter path from 0 to 4, which is 0, 3, 4.
Not your question, but BFS is more suitable for finding shortest paths. I would suggest you rework the algorithm and use BFS instead.
Just to have a complete answer, here is a spoiler solution using BFS:
class Solution:
def shortestAlternatingPaths(self, n, redEdges, blueEdges):
# Build a separate adjacency list for each color
adj = [[[] for _ in range(n)], [[] for _ in range(n)]]
for i, edges in enumerate((redEdges, blueEdges)):
for x, y in edges:
if x or y:
adj[i][x].append(y)
# Collect shortest distances for each color separately
res = [[0] + [-1] * (n-1), [0] + [-1] * (n-1)]
# Start BFS at node 0, traversing with either color
frontier = [(0, 0), (0, 1)]
distance = 1
while frontier: # BFS loop
nextfrontier = []
for node, color in frontier:
for neighbor in adj[color][node]:
# If not yet visited with this color...
if res[color][neighbor] == -1:
res[color][neighbor] = distance
nextfrontier.append((neighbor, 1-color))
frontier = nextfrontier
distance += 1
# Get the minimum distance per node from the two color alternatives
return [min(a, b) if min(a, b) > -1 else max(a, b)
for a, b in zip(*res)]

Sum of Two Arrays - Python

Two random integer arrays/lists have been given as ARR1 and ARR2 of size N and M respectively. Both the arrays/lists contain numbers from 0 to 9(i.e. single digit integer is present at every index). The idea here is to represent each array/list as an integer in itself of digits N and M.
You need to find the sum of both the input arrays/list treating them as two integers and put the result in another array/list i.e. output array/list will also contain only single digit at every index.
NOTE:
The sizes N and M can be different.
Output array/list(of all 0s) has been provided as a function argument. Its size will always be one more than the size of the bigger array/list. Place 0 at the 0th index if there is no carry.
No need to print the elements of the output array/list.
def sumOfTwoArrays(arr1, n, arr2, m, output) :
#Your code goes here
#Taking Input Using Fast I/O
def takeInput() :
n = int(stdin.readline().rstrip())
if n == 0 :
return list(), 0
arr = list(map(int, stdin.readline().rstrip().split(" ")))
return arr, n
#to print the array/list
def printList(arr, n) :
for i in range(n) :
print(arr[i], end = " ")
print()
#main
t = int(stdin.readline().rstrip())
while t > 0 :
arr1, n = takeInput()
arr2, m = takeInput()
outputSize = (1 + max(n, m))
output = outputSize * [0]
sumOfTwoArrays(arr1, n, arr2, m, output)
printList(output, outputSize)
t -= 1
Sample Input:
1
3
6 2 4
3
7 5 6
Sample Output:
1 3 8 0
This problem can be solved by a simple function like this:
(Note - you can made adjustment on the last line of return result if needed to meet the "strange requirement" of - 'place 0 at the 0th index if there is no carry'. It's left as a trivial exercise. )
def sum_two_array(L1, L2):
carry, total = 0, 0
m, n = len(L1), len(L2)
k = max(m, n)
result = [0] + [0] * k # add +1
for i in range(1, k+1):
a = L1[m-i] if m - i >= 0 else 0
b = L2[n-i] if n - i >= 0 else 0
total = a + b + carry
result[k-i + 1] = total % 10
carry = total // 10
if carry > 0: result[0] = carry
return result if result[0] != 0 else result[1:]
if __name__ == '__main__':
L1 = [6, 4, 4]
L2 = [7, 5, 6]
print(sum_two_array(L1, L2)) # [1, 4, 0, 0]
print(sum_two_array([6, 2, 4], [7, 5, 6])) # [1, 3, 8, 0]
print(sum_two_array([1, 2, 4], [8, 0])) # [2, 0, 4]
JAVA CODE:
import java.lang.Math;
public class Solution {
public static void sumOfTwoArrays(int arr1[], int arr2[], int output[]) {
int n = arr1.length; //size of arr1
int m = arr2.length; //size of arr2
int o = n+1; //size of output array
int sum1 = 0, sum2=0, totalSum=0;
//storing sum of arr1 in sum1
for(int i=0; i<n; i++)
{
sum1+= arr1[i] * Math.pow(10,(n-1-i));
}
//storing sum of arr2 in sum2
for(int i=0; i<m; i++)
{
sum2+= arr2[i] * Math.pow(10, (m-1-i));
}
totalSum = sum1+sum2;
//storing totalSum in reverse order in output array
for(int i=o-1; i>=0; i--)
{
output[i] = totalSum % 10;
totalSum = totalSum/10;
}
}
}
Explanation:
condition: arr1[n], arr2[m], output[n+1]
Instead of calculating sum of unit, tens, and so on digits of both the arrays.
we first calculate the sum1 of arr1, and sum2 of arr2, by using:
number at index * (10 ^ ((n-1) - index)) concept.
sum1 and sum2 are equal to the n and m sized numbers of respective arrays
we store totalSum = sum1+sum2
we store totalSum's every digit in output[n+1] array
we store it in reverse order by using % and / operations

Directed Graph: How to find the longest path of Node A to N Considering the ff code:

I have considered using DFS for tracking the vertex for each edges present to find the longest path but the problem is it gives only the L input to it and the edge corresponding to it. I want to find the longest path traversed by these vertices as a alphabetically nondecreasing string and if the string playes indefenitely it outputs infinite. Here is my initial working code:
from collections import defaultdict
def DFS(G,v,seen=None, path=None):
if seen is None: seen = []
if path is None: path = [v]
seen.append(v)
paths =[]
for i in G[v]:
if i not in seen:
i_path = path + [i]
paths.append(tuple(i_path))
paths.extend(DFS(G, i, seen[:], i_path))
return paths
t = int(input())
i,j = 1,0
graph = defaultdict(list)
while i < t:
n, a = [int(i) for i in input().split()]
while j < a:
n1,n2,l = input().split()
graph[l].append(n1)
graph[l].append(n2)
j+= 1
print(graph)
print(DFS(graph, 'A'))
i+=1
i want to know where i did wrong or maybe i missed something essential since the given output for the sample input is:
INPUT:
1 // test case
5 7 // n, a
2 3 B
3 1 E
1 2 A
1 2 R
2 4 D
3 5 E
4 3 D
Expected : ADDER
ACTUAL: D, D

How to compare the elements of a nested list?

I have some kind of matrix:
[[1,2,3],[1,2,3],[0,2,1],[1,2,3]] and I would like to be able to determine how many times I have the sequence 1,2,3 diagonally, vertically and horizontally but I have problems of index out of range for the last two loops. Thank you in advance for your answers!!
lista = [[1,2,3],[1,2,3],[0,2,1],[1,2,3]]
compteur_horizontal = 0
for i in range(len(lista)):
for c in range(len(lista[i])-2):
if lista[i][c] == 1 and lista[i][c+1] == 2 and lista[i][c+2] == 3:
compteur_horizontal += 1
print("ok")
compteur_vertical = 0
for c in range(len(lista)):
for j in range(len(lista[c])):
print(lista[j][c])
compteur_diagonale = 0
for j in range(len(lista)):
print(lista[i][i])
For the first counter, I would like it to be 3 since we have 3 times the sequence 1,2,3 horizontally. For the second counter, I would like it to be 0 because vertically there is no 1,2,3 sequence. And I'm waiting for a counter with 0 also since there's no 1,2,3 sequence in diagonal
Your code will not work even if you correct the current error. You need to change j and c in the second loop to resolve the error.
For horizontal and vertical here's the code. I'll add how to count diagonal ones later.
#lista = [[1, 2, 3],[1, 2, 3],[0, 2, 1],[1, 2, 3]]
lista = [[1,2,2],
[1,2,4],
[4,2,3],
[5,6,3]]
ptrn = [1, 2, 3]
# returns True is lst (1d array) has ptrn (1d array).
# e.g. lst[1,2,4,6,1,2,3,7], ptrn=[1,2,3] return True
# e.g. lst[1,2,4,6,1,4,3,7], ptrn=[1,2,3] return False
def is_pattern_in_a_list(lst, ptrn):
if ptrn[0] in lst:
idx = lst.index(ptrn[0])
for i in range(1, len(ptrn)):
if idx + i < len(lst):
if ptrn[i] != lst[idx+i]:
return False
else:
return False
return True
else:
return False
# counting horizontal occurances
count_h = 0
for row in lista:
if is_pattern_in_a_list(row, ptrn):
count_h += 1
# counting vertical occurances
# we first transpose the 2d array and use the same
# method of counting horizontal occurances.
def transpose_2d_array(a):
return [[a[j][i] for j in range(len(a))] for i in range(len(a[0]))]
lista_transpose = transpose_2d_array(lista)
count_v = 0
for row in lista_transpose:
if is_pattern_in_a_list(row, ptrn):
count_v += 1
# for diagonal occurances first we need to extract the diagonals
# with len >= len(ptrn).
# diagonals for the upper right triangle of the matrix:
count_d = 0
for i in range(len(lista[0])):
diag = []
j = 0
k = i
while j < len(lista)-i and k < len(lista[0]):
diag.append(lista[j][k])
j += 1
k += 1
if is_pattern_in_a_list(diag, ptrn):
count_d += 1
# diagonals for the lower left triangle of the matrix
i = len(lista) - 1
while i >= 1:
j = i
k = 0
diag = []
while j < len(lista) and k <= len(lista)+1-i:
diag.append(lista[j][k])
j += 1
k += 1
i -= 1
if is_pattern_in_a_list(diag, ptrn):
count_d += 1
print(diag)
print("Horizontal %d" % count_h)
print("Vertical %d" % count_v)
print("Diagonal %d" % count_d)

Find multiples of a number in list

I have a list and I want to find all the multiples of that number within a certain tolerance as well as get their indices:
def GetPPMError(t, m):
"""
calculate theoretical (t) and measured (m) ppm
"""
return (((t - m) / t) * 1e6)
multiple = n*1.0033
a = 100
b = [100, 101, 101.0033,102, 102.0066,102.123,103.0099]
results = [p for p in b if abs(GetPPMError(a,b)) < 10]
So I want to find all the multiples like 102.0066 and 103.0099 etc.
where a = 100 + 1*1.0033, a = 100 + 2*1.0033, a = 100 + 3*1.0033 etc
So the result would be the indexes.
Results for the indexes:
[2, 4, 6]
and:
[101.0033, 102.0066, 103.0099]
for the values.
This works for your data:
multiple = 1.0033
a = 100
digits = 6
res = []
res_index = []
for n, x in enumerate(b):
diff = round((x - a) / multiple, digits)
if diff != 0 and diff == int(diff):
res.append(x)
res_index.append(n)
print(res)
print(res_index)
Output:
[101.0033, 102.0066, 103.0099]
[2, 4, 6]

Resources