Creating a graph from the list

For Q1, you can go through the matrix row by row and column by column forming groups using lists associated with the coordintate. Place each coordinate in the list of the connecting positions above or to the left of the current one. Create a new list (group) when there are no connections, Merge the lists when the position connects two different groups.

a=[
   ["x", "x", 0, 0, "x","x"],
   [ 0,   0,  0, 0, "x","x"],
   ["x", "x", 0, 0,  0, "x"],
   ["x", "x", 0, 0,  0,  0 ]
]

groups = dict()                    # group (list) at each coordinate
for r,row in enumerate(a):
    for c,v in enumerate(row):
        above = groups[r-1,c] if r and a[r-1][c] == v else None     
        left  = groups[r,c-1] if c and a[r][c-1] == v else None
        if above and left and above is not left:
            above.extend(left)     # merge left into above group
            for rc in left: groups[rc] = above             
        g = above or left or []    # select existing group or new
        g.append((r,c))            # add r,c to selected group
        groups[r,c] = g            # identify coordinate's group

# extract distinct groups from coordinate assignments
groups = list({id(g):g for g in groups.values()}.values())

Output:

# all groups (i.e. groups of "x"s and groups of 0s)
print(groups)
[[(0, 1), (0, 0)], 
 [(1, 2), (3, 2), (1, 3), (3, 5), (3, 3), (0, 2), 
  (2, 4), (2, 3), (2, 2), (1, 0), (3, 4), (1, 1), (0, 3)], 
 [(1, 4), (1, 5), (0, 5), (0, 4), (2, 5)], 
 [(3, 0), (2, 0), (3, 1), (2, 1)]]

# only "x" groups
xGroups = [g for g in groups if a[g[0][0]][g[0][1]] == "x"]
print(xGroups)
[[(0, 1), (0, 0)], 
 [(1, 4), (1, 5), (0, 5), (0, 4), (2, 5)], 
 [(3, 0), (2, 0), (3, 1), (2, 1)]]

For Q2, you can write a function that will find the distance between two groups by expanding on the paths that each coordinate of the first group can go through to reach any coordinate of the second group while only going through 0 positions (technically a breadth-first search):

def distance(a,g1,g2):
    rows = len(a)
    cols = len(a[0])
    distance = 1
    seen     = set(g1) # track visited coordinates
    paths    = g1      # current reach, starts with g1 coordinates
    while paths:
        nextPaths = set()
        for r,c in paths:
            for nr,nc in [(r,c-1),(r,c+1),(r-1,c),(r+1,c)]: #neighbours
                if nr not in range(rows): continue
                if nc not in range(cols): continue
                if (nr,nc) in seen: continue        # no doubleback
                if (nr,nc) in g2: return distance   # g2 reached
                if a[nr][nc] == 0:
                    nextPaths.add((nr,nc))          # advance on 0s
                    seen.add((nr,nc))
        distance += 1                  # g2 not reached, need more steps
        paths = nextPaths              # start from positions reached so far
    return None

print(distance(a,xGroups[0],xGroups[1])) # 3
print(distance(a,xGroups[1],xGroups[2])) # 4            
print(distance(a,xGroups[0],xGroups[2])) # 2 

I'm posting a slightly less efficient, but perhaps better readable algotithm for the Q1.

It starts with any arbitrary "X" and then adds its neighbours and then neighbours of the neigbours etc until there are no more neighbours, i.e. the whole group of adjacent "X"s is found.

def neigh4(c):
    i, j = c 
    yield (i-1, j)
    yield (i+1, j)
    yield (i, j-1)
    yield (i, j+1)

def groups(array):
    coords = { 
        (i,j) for i in range(len(array)) for j in range(len(array[0]))
        if array[i][j] == "x"}
    while coords:
        group = set()
        wset = {next(iter(coords))} # arbitrary member
        while wset:
            member = wset.pop()
            wset.update(c for c in neigh4(member) if c in coords)
            coords.remove(member)
            group.add(member)
        yield group

print(list(groups(a))) # "a" from the question

Personal note: Over 30 years ago we were given this excercise in a programming competition (in BASIC!). I was 15 years old and failed to solve it.