Fox Ciel is playing a mobile puzzle game called "Two Dots". The basic levels are played on a board of size n × m cells, like this:

[Image]

Each cell contains a dot that has some color. We will use different uppercase Latin characters to express different colors.

The key of this game is to find a cycle that contain dots of same color. Consider 4 blue dots on the picture forming a circle as an example. Formally, we call a sequence of dots d_1, d_2, ..., d_{k} a cycle if and only if it meets the following condition:

  These k dots are different: if i ≠ j then d_{i} is different from d_{j}.  k is at least 4.  All dots belong to the same color.  For all 1 ≤ i ≤ k - 1: d_{i} and d_{i} + 1 are adjacent. Also, d_{k} and d_1 should also be adjacent. Cells x and y are called adjacent if they share an edge. 

Determine if there exists a cycle on the field.


-----Input-----

The first line contains two integers n and m (2 ≤ n, m ≤ 50): the number of rows and columns of the board.

Then n lines follow, each line contains a string consisting of m characters, expressing colors of dots in each line. Each character is an uppercase Latin letter.


-----Output-----

Output "Yes" if there exists a cycle, and "No" otherwise.


-----Examples-----
Input
3 4
AAAA
ABCA
AAAA

Output
Yes

Input
3 4
AAAA
ABCA
AADA

Output
No

Input
4 4
YYYR
BYBY
BBBY
BBBY

Output
Yes

Input
7 6
AAAAAB
ABBBAB
ABAAAB
ABABBB
ABAAAB
ABBBAB
AAAAAB

Output
Yes

Input
2 13
ABCDEFGHIJKLM
NOPQRSTUVWXYZ

Output
No



-----Note-----

In first sample test all 'A' form a cycle.

In second sample there is no such cycle.

The third sample is displayed on the picture above ('Y' = Yellow, 'B' = Blue, 'R' = Red).

Difficulty: interview

Starter code:

Solutions:
#!/usr/bin/env python

import sys

sys.setrecursionlimit(10000)

n, m = list(map(int, input().split(' ')))

def neighbors(i, j):
    return [(i+1, j), (i-1, j), (i, j+1), (i, j-1)]

def valid(i, j):
    nonlocal n, m
    if i < 0 or i >= n or j < 0 or j >= m:
        return False
    return True

def dfs(f, i, j):
    color = f[i][j]
    f[i][j] = color.lower()
    c = 0
    for n, m in neighbors(i, j):
        if valid(n, m):
            if f[n][m] == color:
                cycle_found = dfs(f, n, m)
                if cycle_found:
                    return True
            elif f[n][m] == color.lower():
                c += 1
    if c > 1:
        return True

    f[i][j] = None

    return False

f = []
for i in range(n):
    f.append(list(input().strip()))

for i in range(n):
    for j in range(m):
        if f[i][j]:
            cycle_found = dfs(f, i, j)
            if cycle_found:
                print("Yes")
                return

print("No")


import sys
sys.setrecursionlimit(10000)

n, m = map(int, input().split())
A = []

for i in range(n):
    A.append(input() + '1')

A.append('1' * (m + 1))
ans = 0
used = [[False] * m for i in range(n)]
B = ''.join(A)
B = list(set(B))
def dfs(i, j, b, c):
    nonlocal ans
    used[i][j] = True
    
    if A[i - 1][j] == b and c != 3:
        if used[i - 1][j]:
            ans = 1
        else:
            dfs(i - 1, j, b, 1)

    if A[i][j + 1] == b and c != 4:
        if used[i][j + 1]:
            ans = 1
        else:
            dfs(i, j + 1, b, 2)

    if A[i + 1][j] == b and c != 1:
        if used[i + 1][j]:
            ans = 1
        else:
            dfs(i + 1, j, b, 3)

    if A[i][j - 1] == b and c != 2:
        if used[i][j - 1]:
            ans = 1
        else:
            dfs(i, j - 1, b, 4)

for i in range(len(B)):
    used = [[False] * m for i in range(n)]
    if B[i] != '1':
        b = B[i]
        for x in range(n):
            for y in range(m):
                if A[x][y] == b and not used[x][y]:
                    dfs(x, y, b, -1)

if ans == 0:
    print('No')
else:
    print('Yes')

import math
ln, cl = (int(x) for x in input().split())

in_dt = []

for i in range(ln):
    in_dt.append(input())

def is_adj(x_1, y_1, x_2, y_2):
    if math.abs(x_2 - x_1) == 1 and y_2 - y_1 == 0:
        return True
    elif math.abs(y_2 - y_1) == 1 and x_2 - x_1 == 0:
        return True
    return False

ch_dt = [[0 for x in range(55)] for y in range(55)]

def neighb(x, y, ln, cl, lst):
    res = []
    if x - 1 >= 0 and lst[x][y] == lst[x - 1][y]:
        res.append((x - 1, y))
    if y - 1 >= 0 and lst[x][y] == lst[x][y - 1]:
        res.append((x, y - 1))
    if x + 1 < ln and lst[x][y] == lst[x + 1][y]:
        res.append((x + 1, y))
    if y + 1 < cl and lst[x][y] == lst[x][y + 1]:
        res.append((x, y + 1))
    return res

path = []

def go_path(lst, path):
    nonlocal ln
    nonlocal cl
    while True:
        if len(path) == 0:
            break
        cur_x = path[-1][0]
        cur_y = path[-1][1]
        del path[-1]
        #print (cur_x, cur_y)
        #print (path)
        neighbours = neighb(cur_x, cur_y, ln, cl, lst)
        #print(neighbours)
        if len(neighbours) != 0:
            for neighbour in neighbours:
                if neighbour in path:
                    return True
                # print ("_______")
                #print (ch_dt)
                #print (neighbour)
                #print ("+++++++")
                if ch_dt[neighbour[0]][neighbour[1]] == 0: 
                    path.append(neighbour)
                    ch_dt[neighbour[0]][neighbour[1]] = 1
        #print(path)
    #print (False)
    return False
for i in range(ln):
    for j in range(cl):
        if ch_dt[i][j] == 0:
            ch_dt[i][j] = 1
            path.append((i, j))
            if go_path(in_dt, path):
                print ("Yes")
                return
print("No")


n, m = map(int, input().split())
tabl = []
for l in range(n):
    tabl.append(list(input()))
def lstsos(i, j):
    nonlocal n, m
    ne = [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
    for l, k in ne:
        if 0 <= l < n and 0 <= k < m:
            yield (l, k)
def degree(i, j):
    deg = 0
    for l, k in lstsos(i, j):
        deg += (tabl[l][k] == tabl[i][j])
    return deg
finished = False
while not finished:
    finished = True
    for l in range(n):
        for k in range(m):
            if tabl[l][k] != 'NO' and degree(l, k) in (0, 1):
                tabl[l][k] = 'NO'
                finished = False
cycle = False
for l in range(n):
    for k in range(m):
        if tabl[l][k] != 'NO':
            cycle = True
if cycle:
    print ("Yes")
else:
    print ("No")

n, m = [int(x) for x in input().split()]

field = [""] * n
labels = [[0]] * n

import sys
sys.setrecursionlimit(10000)

for i in range(n):
    field[i] = input().strip()
    labels[i] = [0] * m

def is_valid(i, j):
    return 0 <= i < n and 0 <= j < m


def dfs(i, j, p, k, cc = 0):
    if labels[i][j] == k:
        return True
    labels[i][j] = k

    res = False
    dd = [[0, 1], [1, 0], [-1, 0], [0, -1]]
    for d in dd:
        if is_valid(i + d[0], j + d[1]) and (i + d[0] != p[0] or j + d[1] != p[1]):
            if field[i + d[0]][j + d[1]] == field[i][j]:
                res = res or dfs(i + d[0], j + d[1], [i, j], k, cc + 1)

    return res

ok = False
k = 1
for i in range(n):
    for j in range(m):
        if labels[i][j] == 0:
            ok = ok or dfs(i, j, [i, j], k)
            k += 1

if ok:
    print("Yes")
else:
    print("No")

def main():
    n, m = list(map(int, input().split()))
    l = [c for _ in range(n) for c in input()]
    neigh = []
    for y in range(n):
        for x in range(m):
            yx = y * m + x
            neigh.append([])
            if x and l[yx] == l[yx - 1]:
                neigh[-1].append(yx - 1)
                neigh[-2].append(yx)
            if y and l[yx] == l[yx - m]:
                neigh[-1].append(yx - m)
                neigh[-1 - m].append(yx)
    field = [0] * len(l)

    def dfs(t):
        field[t] = 1
        for v in neigh[t]:
            if not field[v]:
                neigh[v].remove(t)
                dfs(v)
            elif field[v] == 1:
                raise OverflowError
        field[t] = 2

    for i, flag in enumerate(field):
        if not flag:
            try:
                dfs(i)
            except OverflowError:
                print('Yes')
                return
    print('No')


def __starting_point():
    import sys

    sys.setrecursionlimit(10000)
    main()

__starting_point()

#f = lambda: input()
#file = open("btest", "r")
#f = lambda:file.readline()

# n, m = map(int, f().split())

# g = []

# for i in range(n):
#     g.append(f())

# disco = [[False for _ in range(m)] for _ in range(n)]

# #print(disco)
# #print(parent)
          
# def found_cycle(w):
#     nonlocal disco
#     s = []
#     s.append(w)
#     while s:
#         v = s.pop()
#         if disco[v[0]][v[1]] == False:
#             if v[0] > 0 and g[v[0]-1][v[1]] == g[v[0]][v[1]]:
#                 s.append([v[0]-1, v[1]])
                
#             if v[0] < n-1 and g[v[0]+1][v[1]] == g[v[0]][v[1]]:
#                 s.append([v[0]+1, v[1]])
                
#             if v[1] > 0 and g[v[0]][v[1]-1] == g[v[0]][v[1]]:
#                 s.append([v[0], v[1]-1])
                
#             if v[1] < m-1 and g[v[0]][v[1]+1] == g[v[0]][v[1]]:
#                 s.append([v[0], v[1]+1])
                
#         else:
#             disco[v[0]][v[1]] = True
#     return False

# found = False
# for i in range(n):
#     if found:
#         break
#     for j in range(m):
#         if disco[i][j] == False:
#             found = found_cycle([i,j])
#             break

# if found:
#     print ("Yes")
# else:
#     print ("No")

#Editorial
import sys
sys.setrecursionlimit(10000)

#file = open("btest", "r")
#f = lambda:file.readline()
f = lambda:input()

n,m = list(map(int, f().split()))

matrix = []

disco = [[0 for _ in range(m)] for _ in range(n)]

for i in range(n):
    matrix.append(f())

def valid_node(i, j):
    return 0 <= i < n and 0 <= j < m

def dfs(i, j, p, k, cc = 0):
    if disco[i][j] == k:
        return True
    
    disco[i][j] = k

    res = False
    dd = [[0,1], [1,0], [-1,0], [0,-1]]
    for d in dd:
        if valid_node(i+ d[0], j+d[1]) and (i + d[0] != p[0] or j + d[1] != p[1]):
            if matrix[i + d[0]][j+ d[1]] == matrix[i][j]:
                res = res or dfs(i + d[0], j+d[1], [i,j], k, cc+1)

    return res

ok = False
k = 1
for i in range(n):
    for j in range(m):
        if disco[i][j] == 0:
            ok = ok or dfs(i, j, [i, j], k)
            k += 1

if ok:
    print("Yes")
else:
    print("No")


import sys
#sys.stdin=open('in.txt')
sys.setrecursionlimit(3000)
isin = lambda x, y : 0<=x<m and 0<=y<n

def dfs(curx, cury, fax=-1, fay=-1):
    nonlocal found
    vis[curx][cury]=True
    dx=[0, 0,1,-1]
    dy=[1,-1,0, 0]
    for i in range(4):
        newx=curx+dx[i]
        newy=cury+dy[i]
        if isin(newx,newy) and vis[newx][newy] and G[curx][cury]==G[newx][newy] and not (fax==newx and fay == newy):
            found=True
        if isin(newx,newy) and not vis[newx][newy] and G[curx][cury]==G[newx][newy] :
            dfs(newx,newy,curx, cury)

m,n = list(map(int, input().split()))
vis=[[False]*n for i in range(m)]
found=False
G=[input() for i in range(m)]
for i in range(m):
    for j in range(n):
        found=False
        if not vis[i][j]:
            dfs(i,j)
        if found:
            print('Yes')
            return

print('No')


import sys
#sys.stdin=open('in.txt')
sys.setrecursionlimit(10000)
isin = lambda x, y: 0<=x<m and 0<=y<n

def dfs(curx, cury, fax=-1, fay=-1):
    nonlocal found
    vis[curx][cury]=True
    dx=[0, 0, 1, -1]
    dy=[1, -1, 0, 0]
    for i in range(4):
        newx=curx+dx[i]
        newy=cury+dy[i]
        if isin(newx,newy) and vis[newx][newy] and G[curx][cury]==G[newx][newy] and not (fax == newx and fay == newy):
            found=True
        if isin(newx,newy) and not vis[newx][newy] and G[curx][cury]==G[newx][newy]:
            dfs(newx, newy, curx, cury)   

m,n = list(map(int, input().split()))
vis=[[False]*n for i in range(m)]
found=False
G=[input() for i in range(m)]
for i in range(m):
    for j in range(n):
        found = False
        if not vis[i][j]:
            dfs(i, j)
        if found:
            print('Yes')
            return
print('No')    


import sys
#sys.stdin=open('in.txt')
sys.setrecursionlimit(10000)
isin = lambda x, y: 0<=x<m and 0<=y<n

def dfs(curx, cury, fax=-1, fay=-1):
    nonlocal found
    vis[curx][cury]=True
    dx=[0, 0, 1, -1]
    dy=[1, -1, 0, 0]
    for i in range(4):
        newx=curx+dx[i]
        newy=cury+dy[i]
        if isin(newx,newy) and vis[newx][newy] and G[curx][cury]==G[newx][newy] and not (fax == newx and fay == newy):
            found=True
            return
        if isin(newx,newy) and not vis[newx][newy] and G[curx][cury]==G[newx][newy]:
            dfs(newx, newy, curx, cury)   

m,n = list(map(int, input().split()))
vis=[[False]*n for i in range(m)]
found=False
G=[input() for i in range(m)]
for i in range(m):
    for j in range(n):
        found = False
        if not vis[i][j]:
            dfs(i, j)
        if found:
            print('Yes')
            return
print('No')    


import sys
sys.setrecursionlimit(3000)

n,m = map(int, input().split(' '))
L = []
dx = [-1, 0, 0, 1]
dy = [0, -1, 1, 0]
for i in range(n) :
    L.append(input())

def dfs(y, x, p_y, p_x, visited) :
    nonlocal dx, dy, n, m, L
    visited[y][x] = 1
    for i in range(4) :
        new_y = y + dy[i]
        new_x = x + dx[i]
        if new_y < 0 or new_y >= n or new_x < 0 or new_x >= m : continue
        if new_y == p_y and new_x == p_x : continue
        if L[y][x] == L[new_y][new_x] :
            if visited[new_y][new_x] == 1: return True
            if dfs(new_y, new_x, y, x, visited) : return True
    return False

def solve() :
    nonlocal n,m,L
    visited = [[0 for j in range(m)] for i in range(n)]
    for i in range(n) :
        for j in range(m) :
            if visited[i][j] == 1:continue
            if dfs(i, j, -1, -1, visited) :
                return True
    return False

if solve() : print("Yes")
else : print("No")

import sys
#sys.stdin = open('in.txt')
sys.setrecursionlimit(10000)
isin = lambda x, y: 0 <= x < n and 0 <= y < m

def dfs(curx, cury, fax = -1, fay = -1):
    nonlocal found
    vis[curx][cury] = True
    dx = [1, -1, 0, 0]
    dy = [0, 0, 1, -1]
    for i in range(4):
        newx = curx + dx[i]
        newy = cury + dy[i]
        if isin(newx, newy):
            if G[curx][cury] == G[newx][newy] and vis [newx][newy] and not (newx == 

fax and newy == fay):
                found = True
                print("Yes")
                return
            if G[curx][cury] == G[newx][newy] and not vis[newx][newy]:
                dfs(newx, newy, curx, cury)

n, m = map(int, input().split())
vis = [[False] * m for _ in range(n)]
G = [input() for _ in range(n)]
found = False
for i in range(n):
    for j in range(m):
        if not vis[i][j]:
            dfs(i, j)
        if found:
            print('Yes')
            return
print('No')

import sys
def dfs(i,j,vis,out,mapp,s=2):
    if vis[i][j] and vis[i][j]!= s-1:
        return True
    t=[(0,1),(1,0),(-1,0),(0,-1)]
    vis[i][j]=s
    out[i][j]=True
    for k in t:
        try:
            (u,v)=(k[0]+i,k[1]+j)
            if u<0 or v<0:
                continue
            if vis[u][v]!=s-1 and mapp[u][v]==mapp[i][j] and dfs(u,v,vis,out,mapp,s+1):
                return True
        except Exception:
            pass
    vis[i][j]=0
    return False
sys.setrecursionlimit(2500)
(n,m)=input().split()
(n,m)=(int(n),int(m))
exist=set()
mapp=[0 for i in range(n)]
for i in range(n):
    mapp[i]=input()
for i in range(n):
    for j in range(m):
        exist.add(mapp[i][j])
key=0
t=[(0,1),(1,0),(-1,0),(0,-1)]
out=[[False for i in range(m)] for j in range(n)]
for c in exist:
    if key:
        break
    vis=[[0 for i in range(m)] for j in range(n)]
    for i in range(n):
        if key:
            break
        for j in range(m):
            if key:
                break
            if mapp[i][j]==c and not out[i][j] and dfs(i,j,vis,out,mapp):
                key=1
                break
if key:
    print('Yes')
else:
    print('No')
                            
                


def q():
    n,m=map(int,input().split())
    p=[k for _ in range(n) for k in input()]
    w=[]
    for z in range(n):
        for j in range(m):
            ds=z*m+j
            w.append([])
            if j and p[ds]==p[ds-1]:
                w[-1].append(ds-1)
                w[-2].append(ds)
            if z and p[ds]==p[ds-m]:
                w[-1].append(ds-m)
                w[-1-m].append(ds)
    pol=[0]*len(p)
    def zss(g):
            pol[g]=1
            for f in w[g]:
                if not pol[f]:
                    w[f].remove(g)
                    zss(f)
                elif pol[f]==1:
                    raise OverflowError
            pol[g]=2
    for i,flag in enumerate(pol):
        if not flag:
            try:
                zss(i)
            except OverflowError:
                print('Yes')
                return
    print('No')
if 1==1:
    import sys
    sys.setrecursionlimit(10000)
    q()

t, u = list(map(int, input().split(' ')))

def ok(x, y, inx):
    if not (0 <= x <= t-1 and 0 <= y <= u-1):
        return False
    if not inx[x][y]:
        return False
    return True
        


def dfs(graph, start, path=[]):
    inx = [[False] * u for _ in range(t)]
    for i in graph:
        a, b = i[0], i[1]
        inx[a][b] = True
    visited = [[False] * u for _ in range(t)]
    parent = [[0] * u for _ in range(t)]
    
    q=[start]
    
    while q:
        
        bd = q.pop(0)
        a = bd[0]
        b = bd[1]

        if not visited[a][b]:
            for bad in [(a-1, b), (a, b-1), (a+1, b), (a, b+1)]:
                x = bad[0]
                y = bad[1]
                if ok(x, y, inx):
                    if visited[x][y] and (x, y) != parent[a][b]:
                        return True
                    q=[(x, y)]+q
                    parent[x][y] = (a, b)
            visited[a][b] = True
    return False

graph = {}
for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
    graph[i] = []

for i in range(t):
    x = input()
    for j in range(u):
        graph[x[j]].append((i, j))

graph2 = {}
for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
    if graph[i] != []:
        graph2[i] = graph[i]

for i in graph2:
    for e in graph2[i]:
        if dfs(graph2[i], e):
            print("Yes")
            quit()


print("No")


import sys
sys.setrecursionlimit(10000)

def f(i, j):
    return -1 < i < n and -1 < j < m


def dfs(c, r, l):
    used[c][r] = 1
    for (i, j) in {(0, 1), (-1, 0), (1, 0), (0, -1)}:
        if f(c + i, r + j) and a[c + i][r + j] == a[c][r]:
            if used[c + i][r + j] == 0:
                prev[c + i][r + j] = (c, r)
                dfs(c + i, r + j, l + 1)
            elif used[c + i][r + j] == 1 and prev[c][r] != (c + i, r + j):
                nonlocal s 
                s = "Yes"
    used[i][j] = 2
            


n, m = map(int, input().split())
a = []
s = "No"
for i in range(n):
    a.append(list(input().rstrip()))

used = [[0 for i in range(m)] for i in range(n)]
prev = [[-1 for i in range(m)] for i in range(n)]
for i in range(n):
    for j in range(m):
        if used[i][j] == 0:
            dfs(i, j, 0)
print(s)

import sys
sys.setrecursionlimit(10000)

n,m = map(int, input().split())

board = []
for _ in range(n):
    board.append(input().rstrip())

visited = [[False for _ in range(m)] for _ in range(n)]
foundCycle = False
    
dx = [1,-1,0,0]
dy = [0,0,1,-1]

def dfs(x, y, fromX, fromY, color):
    nonlocal board, visited, foundCycle

    if x < 0 or x >= n or y < 0 or y >= m: 
        return
    if board[x][y] != color: 
        return

    if visited[x][y]:
        foundCycle = True
        return
    
    visited[x][y] = True
    for f in range(4):
        nextX = x + dx[f]
        nextY = y + dy[f]
        if nextX == fromX and nextY == fromY:
            continue
        dfs(nextX, nextY, x, y, color)
        
for i in range(n):
    for j in range(m):
        if not visited[i][j]:
            dfs(i,j,-1,-1, board[i][j])
print("Yes" if foundCycle else "No")

msg_in = input().split()
x = int(msg_in[0])
y = int(msg_in[1])
num_of_transform = [0, 0]
matrix = []
blank = []
for i in range(y+2):
    blank.append(' ')
matrix.append(blank)
for i in range(x):
    msg_in = input()
    matrix.append([' '])    
    for j in msg_in:
        matrix[i+1].append(j)
    matrix[i+1].append(' ')
matrix.append(blank)
while True:
    for i in range(1, x + 1):
        for j in range(1, y + 1):
            sum = 0
            if matrix[i][j] != ' ':
                if matrix[i][j-1] == matrix[i][j]:
                    sum += 1
                if matrix[i][j+1] == matrix[i][j]:
                    sum += 1
                if matrix[i-1][j] == matrix[i][j]:
                    sum += 1
                if matrix[i+1][j] == matrix[i][j]:
                    sum += 1
                if sum < 2:
                    matrix[i][j] = ' '
                    num_of_transform[0] += 1
    if num_of_transform[0] == x * y:
        print('No')
        break
    elif num_of_transform[0] == num_of_transform[1]:
        print('Yes')
        break
    else:
        num_of_transform[1] = num_of_transform[0]


# 510B
import string

__author__ = 'artyom'

n, m = map(int, input().split())
graph = []
for _ in range(n):
  graph.append(input())


def neighbours(vertex, colour):
  x, y = vertex
  res = []
  if x > 0 and graph[x - 1][y] == colour:
    res.append((x - 1, y))
  if y > 0 and graph[x][y - 1] == colour:
    res.append((x, y - 1))
  if x < n - 1 and graph[x + 1][y] == colour:
    res.append((x + 1, y))
  if y < m - 1 and graph[x][y + 1] == colour:
    res.append((x, y + 1))
  return res


def find_cycle(colour, start):
  visited = set()
  stack = [(start, None)]
  while stack:
    v, parent = stack.pop()
    if v in visited:
      return set()
    visited.add(v)
    for u in neighbours(v, colour):
      if u != parent:
        stack.append((u, v))
  return visited


for c in string.ascii_uppercase:
  visited = set()
  for i in range(n):
    for j in range(m):
      if (i, j) not in visited and graph[i][j] == c:
        t = find_cycle(c, (i, j))
        if not t:
          print('Yes')
          return
        visited |= t

print('No')

# 510B
__author__ = 'artyom'

n, m = map(int, input().split())
graph = []
for _ in range(n):
  graph.append(input())


def neighbours(vertex, colour):
  x, y = vertex
  res = []
  if x > 0 and graph[x - 1][y] == colour:
    res.append((x - 1, y))
  if y > 0 and graph[x][y - 1] == colour:
    res.append((x, y - 1))
  if x < n - 1 and graph[x + 1][y] == colour:
    res.append((x + 1, y))
  if y < m - 1 and graph[x][y + 1] == colour:
    res.append((x, y + 1))
  return res


def find_cycle(x, y):
  colour = graph[x][y]
  visited = set()
  stack = [((x, y), None)]
  while stack:
    v, parent = stack.pop()
    if v in visited:
      return set()
    visited.add(v)
    for u in neighbours(v, colour):
      if u != parent:
        stack.append((u, v))
  return visited


visited = set()
for i in range(n):
  for j in range(m):
    if (i, j) not in visited:
      t = find_cycle(i, j)
      if not t:
        print('Yes')
        return
      visited |= t

print('No')

# coding=utf-8
import sys

sys.setrecursionlimit(10000)
g = []

n, m = list(map(int, input().split()))
idx = [[False for i in range(m)] for j in range(n)]
for k in range(n):
    g.append(input())


def dfs(i, j, li, lj):
    idx[i][j] = True
    if i + 1 < n and g[i][j] == g[i + 1][j]:
        if idx[i + 1][j] is False:
            return dfs(i + 1, j, i, j)
        elif not (li == i and lj == j) and i + 1 != li:
            return 'Yes'
    if i - 1 >= 0 and g[i][j] == g[i - 1][j]:
        if idx[i - 1][j] is False:
            return dfs(i - 1, j, i, j)
        elif not (li == i and lj == j) and i - 1 != li:
            return 'Yes'
    if j - 1 >= 0 and g[i][j] == g[i][j - 1]:
        if idx[i][j - 1] is False:
            return dfs(i, j - 1, i, j)
        elif not (li == i and lj == j) and j - 1 != lj:
            return 'Yes'
    if j + 1 < m and g[i][j] == g[i][j + 1]:
        if idx[i][j + 1] is False:
            return dfs(i, j + 1, i, j)
        elif not (li == i and lj == j) and j + 1 != lj:
            return 'Yes'
    return None


for i in range(n):
    for j in range(m):
        if not idx[i][j]:
            ans = dfs(i, j, i, j)
            if ans:
                print(ans)
                break
    else:
        continue
    break
else:
    print('No')


#A
'''
n, m = map(int, input().split())
row = ['#' * m if i % 2 == 0 else '.' * (m - 1) + '#' if i % 4 == 1
       else '#' + '.' * (m - 1) for i in range(n)]
print('\n'.join(row))
'''
#B
import sys
sys.setrecursionlimit(5000)
n, m = list(map(int, input().split()))
dx = [-1, 0, 0, 1]
dy = [0, -1, 1, 0]
L = list(input() for i in range(n))

def check(x, y, px, py):
    return (x >= 0 and x < n and y >= 0 and y < m)
            

def DFS(x, y, px, py, visited):
    #print(x, y, px, py, '\n', visited)
    visited[x][y] = True
    for i in range(4):
        newx = x + dx[i]
        newy = y + dy[i]
        if not check(newx, newy, px, py): continue
        if (newx == px and newy == py): continue
        #print('Bug ', visited)
        if L[x][y] == L[newx][newy]:
            if visited[newx][newy]:
                #print('Here True', newx, newy, x, y)
                return True
            if DFS(newx, newy, x, y, visited):
                #print(newx, newy, x, y, visited)
                return True
    return False
        
def solve():
#    visited = [[False] * m] * n
    visited = [[False for j in range(m)] for i in range(n)]    
    for i in range(n):
        for j in range(m):
            if visited[i][j]: continue
            if DFS(i, j, -1, -1, visited): return True
    return False

print('Yes' if solve() else 'No')
'''
3 3
AAA
ABA
ABA
'''