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).

Source: codeforces | Difficulty: MEDIUM
Tags: Graph traversal


Solutions:
__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')
				exit()
			visited |= t
print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached
Detect = False
vertex = {}

def depth_first_search_v2(g, curr, prev):
	global Detect
	global vertex
	if Detect:
		return
	vertex[curr] = True
	for succ in g.neighbours(curr):
		if vertex[succ] and succ != prev:
			Detect = True
			return
		if not vertex[succ]:
			depth_first_search_v2(g, succ, curr)

def cycle(g):
	for i in g.vertices():
		vertex[i] = False
	for j in g.vertices():
		if not vertex[j]:
			depth_first_search_v2(g, j, j)
		if Detect:
			break
	return
(row, col) = map(int, input().split(' '))
rows = []
for i in range(row):
	rows.append(input())
node = {}
for i in range(row):
	for j in range(col):
		node[i * col + j] = rows[i][j]
result_Graph = UndirectedGraph()
for i in range(row * col):
	result_Graph.add_vertex(i)
	try:
		if node[i] == node[i + 1] and i % col != col - 1:
			result_Graph.add_edge(i, i + 1)
		if node[i] == node[i + col]:
			result_Graph.add_edge(i, i + col)
	except:
		continue
cycle(result_Graph)
if Detect:
	print('Yes')
if not Detect:
	print('No')


import sys
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')


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

def cycle_exists(g):
	marked = {u: False for u in g.vertices()}
	found_cycle = [False]
	for u in g.vertices():
		if not marked[u]:
			dfs_visit(g, u, found_cycle, u, marked)
		if found_cycle[0]:
			break
	return found_cycle[0]

def dfs_visit(g, u, found_cycle, pred_node, marked):
	if found_cycle[0]:
		return
	marked[u] = True
	for v in g.neighbours(u):
		if marked[v] and v != pred_node:
			found_cycle[0] = True
			return
		if not marked[v]:
			dfs_visit(g, v, found_cycle, u, marked)
(rowNum, colNum) = map(int, input().split(' '))
rows = []
nodeDict = {}
for i in range(rowNum):
	rows.append(input())
for i in range(rowNum):
	for j in range(colNum):
		nodeDict[i * colNum + j] = rows[i][j]
twoDotsGraph = UndirectedGraph()
for v in range(rowNum * colNum):
	twoDotsGraph.add_vertex(v)
	try:
		if nodeDict[v] == nodeDict[v + 1] and v % colNum != colNum - 1:
			twoDotsGraph.add_edge(v, v + 1)
		if nodeDict[v] == nodeDict[v + colNum]:
			twoDotsGraph.add_edge(v, v + colNum)
	except:
		continue
if cycle_exists(twoDotsGraph):
	print('Yes')
else:
	print('No')


import sys
import string
import math
from collections import defaultdict
from functools import lru_cache
from collections import Counter
sys.setrecursionlimit(3000)

def mi(s):
	return map(int, s.strip().split())

def lmi(s):
	return list(mi(s))

def tmi(s):
	return tuple(mi(s))

def mf(f, s):
	return map(f, s)

def lmf(f, s):
	return list(mf(f, s))

def js(lst):
	return ' '.join((str(d) for d in lst))

def line():
	return sys.stdin.readline().strip()

def linesp():
	return line().split()

def iline():
	return int(line())

def mat(n):
	matr = []
	for _ in range(n):
		matr.append(linesp())
	return matr

def matns(n):
	mat = []
	for _ in range(n):
		mat.append([c for c in line()])
	return mat

def mati(n):
	mat = []
	for _ in range(n):
		mat.append(lmi(line()))
	return mat

def pmat(mat):
	for row in mat:
		print(js(row))

def dist(x, y):
	return ((x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2) ** 0.5

def fast_exp(x, n):
	if n == 0:
		return 1
	elif n % 2 == 1:
		return x * fast_exp(x, (n - 1) // 2) ** 2
	else:
		return fast_exp(x, n // 2) ** 2

def in_range(mat, i, j):
	return i >= 0 and i < len(mat) and (j >= 0) and (j < len(mat[i]))

def cycle(mat, i, j, length, first_i, first_j, path):
	if (i, j) == (first_i, first_j) and length >= 4:
		return True
	elif (i, j) == (first_i, first_j) and length > 0:
		return False
	if (i, j) in path:
		return False
	if not in_range(mat, i, j) or mat[i][j] != mat[first_i][first_j]:
		return False
	path.add((i, j))
	found = any([cycle(mat, i + 1, j, length + 1, first_i, first_j, path), cycle(mat, i - 1, j, length + 1, first_i, first_j, path), cycle(mat, i, j - 1, length + 1, first_i, first_j, path), cycle(mat, i, j + 1, length + 1, first_i, first_j, path)])
	return found

def main(mat):
	for i in range(len(mat)):
		for j in range(len(mat[i])):
			if cycle(mat, i, j, 0, i, j, set()):
				print('Yes')
				return
	print('No')
(n, _) = mi(line())
mat = matns(n)
main(mat)


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}
	nodeNumber = 0

	def do_dfs(curr, nodeNumber, start):
		if curr in reached:
			return
		reached[curr] = nodeNumber
		newNodeNumber = nodeNumber + 1
		for succ in g.neighbours(curr):
			do_dfs(succ, newNodeNumber, start)
	do_dfs(v, nodeNumber, v)
	return reached

def checkForLoop(gameMap):
	for w in gameMap.vertices():
		nodes = depth_first_search(gameMap, w)
		for v in nodes:
			if w in gameMap.neighbours(v) and nodes[v] > 2:
				return True
	return False
twoValues = input().split()
n = int(twoValues[0])
m = int(twoValues[1])
gameMap = UndirectedGraph()
lastLine = ''
for e in range(n):
	line = input()
	for k in range(m):
		gameMap.add_vertex((k, e, line[k]))
		if k != 0 and line[k] == line[k - 1]:
			gameMap.add_edge((k, e, line[k]), (k - 1, e, line[k - 1]))
		if e != 0 and line[k] == lastLine[k]:
			gameMap.add_edge((k, e, line[k]), (k, e - 1, lastLine[k]))
	lastLine = line
if checkForLoop(gameMap):
	print('Yes')
else:
	print('No')


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def look_for_cycle(g, row_col):
	marked = {v: False for v in row_col}
	todo = []
	for cord in row_col:
		if marked[cord] == True:
			continue
		else:
			todo.append((cord, cord))
		while todo:
			temp = todo.pop()
			v = temp[0]
			prev = temp[1]
			if marked[v] == False:
				marked[v] = True
			for n in g.neighbours(v):
				if marked[n] == False:
					marked[n] = True
					todo.append((n, v))
				elif n != prev:
					return True
	return False
(n, m) = [int(x) for x in input().split(' ')]
lines = [input() for _ in range(n)]
chr_grid = [j for i in lines for j in i]
row_col = [(i, j) for (i, x) in enumerate(lines) for j in range(len(x))]
data = {row_col[i]: chr_grid[i] for i in range(len(chr_grid))}
grid = UndirectedGraph()
[grid.add_vertex(x) for x in row_col]
[grid.add_edge((i, j), (i, j - 1)) for i in range(n) for j in range(m) if j > 0 and data[i, j] == data[i, j - 1]]
[grid.add_edge((i, j), (i - 1, j)) for i in range(n) for j in range(m) if i > 0 and data[i, j] == data[i - 1, j]]
temp = look_for_cycle(grid, row_col)
if temp == True:
	print('Yes')
else:
	print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
import threading
threading.stack_size(1000000)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

	def depth_first_search(g, v):
		reached = {}

		def do_dfs(curr, prev):
			if curr in reached:
				print('Yes')
				quit()
			reached[curr] = prev
			for succ in g.neighbours(curr):
				if succ != prev:
					do_dfs(succ, curr)
		do_dfs(v, v)
		return reached

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def make_graph(board, num_rows, num_cols):
	g = UndirectedGraph()
	row_idx = 0
	col_idx = 0
	while row_idx + 1 < num_rows:
		col_idx = 0
		while col_idx + 1 < num_cols:
			if board[row_idx][col_idx] == board[row_idx][col_idx + 1]:
				g.add_edge(str(row_idx) + ',' + str(col_idx), str(row_idx) + ',' + str(col_idx + 1))
			if board[row_idx][col_idx] == board[row_idx + 1][col_idx]:
				g.add_edge(str(row_idx) + ',' + str(col_idx), str(row_idx + 1) + ',' + str(col_idx))
			col_idx += 1
		row_idx += 1
	col_idx = 0
	while col_idx + 1 < num_cols:
		if board[row_idx][col_idx] == board[row_idx][col_idx + 1]:
			g.add_edge(str(row_idx) + ',' + str(col_idx), str(row_idx) + ',' + str(col_idx + 1))
		col_idx += 1
	row_idx = 0
	while row_idx + 1 < num_rows:
		if board[row_idx][col_idx] == board[row_idx + 1][col_idx]:
			g.add_edge(str(row_idx) + ',' + str(col_idx), str(row_idx + 1) + ',' + str(col_idx))
		row_idx += 1
	return g
line_in = input().split()
num_rows = int(line_in[0])
num_cols = int(line_in[1])
board = list()
for x in range(num_rows):
	board.append(input())
board_g = make_graph(board, num_rows, num_cols)
vertices = board_g.vertices()
reached = set()
for vertex in vertices:
	if vertex not in reached:
		reached.update(board_g.depth_first_search(vertex))
print('No')


from collections import deque
from sys import setrecursionlimit

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)
setrecursionlimit(10 ** 6)
GLOBAL_NUM = 0

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		if curr not in reached and v in g.neighbours(curr) and (len(reached) >= 3) and (prev != v):
			global GLOBAL_NUM
			GLOBAL_NUM = 1
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached

def check_cycle(g):
	for v in g.vertices():
		depth_first_search(g, v)
		global GLOBAL_NUM
		if GLOBAL_NUM == 1:
			GLOBAL_NUM = 0
			return True
	return False
g = UndirectedGraph()
colours = dict()
counter = 0
l = input().split(' ')
n = int(l[0])
m = int(l[1])
for i in range(n):
	line = input()
	for v in line:
		colours[counter] = v
		g.add_vertex(counter)
		counter += 1
todo = deque(list(g.vertices()))
l = list()
while todo:
	for k in range(m):
		w = todo.popleft()
		l.append(w)
	for e in l[1:]:
		if colours[e] == colours[e - 1]:
			g.add_edge(e, e - 1)
	l.clear()
for e in list(g.vertices())[0:-m]:
	if colours[e] == colours[e + m]:
		g.add_edge(e, e + m)
if check_cycle(g):
	print('Yes')
else:
	print('No')


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
cycleDetected = False
vertexMarked = {}

def cycleExists(g):
	global cycleDetected
	global vertexMarked
	for v in g.vertices():
		vertexMarked[v] = False
	for v in g.vertices():
		if not vertexMarked[v]:
			dfs_modified(g, v, v)
		if cycleDetected:
			break
	return

def dfs_modified(g, curr, prev):
	global cycleDetected
	global vertexMarked
	if cycleDetected:
		return
	vertexMarked[curr] = True
	for succ in g.neighbours(curr):
		if vertexMarked[succ] and succ != prev:
			cycleDetected = True
			return
		if not vertexMarked[succ]:
			dfs_modified(g, succ, curr)
(rowNum, colNum) = map(int, input().split(' '))
rows = []
nodeDict = {}
for i in range(rowNum):
	rows.append(input())
for i in range(rowNum):
	for j in range(colNum):
		nodeDict[i * colNum + j] = rows[i][j]
twoDotsGraph = UndirectedGraph()
for v in range(rowNum * colNum):
	twoDotsGraph.add_vertex(v)
	try:
		if nodeDict[v] == nodeDict[v + 1] and v % colNum != colNum - 1:
			twoDotsGraph.add_edge(v, v + 1)
		if nodeDict[v] == nodeDict[v + colNum]:
			twoDotsGraph.add_edge(v, v + colNum)
	except:
		continue
cycleExists(twoDotsGraph)
if cycleDetected:
	print('Yes')
else:
	print('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]


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def dfs_cycle(g, v):
	reached = set()

	def do_dfs(curr, prev):
		if curr in reached:
			return True
		reached.add(curr)
		to_do = set(g.neighbours(curr))
		to_do.discard(prev)
		if to_do:
			for succ in to_do:
				result = do_dfs(succ, curr)
			return result
		else:
			return False
	return do_dfs(v, v)
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
raw_data = input().split()
rows = int(raw_data[0])
columns = int(raw_data[1])
grid = []
for i in range(rows):
	grid.append(input())
board = UndirectedGraph()
for y in range(rows):
	for x in range(columns):
		board.add_vertex((x, y, grid[y][x]))
d = [(0, 1), (1, 0), (-1, 0), (0, -1)]
x = 0
y = 1
code = 2
for node in board.vertices():
	for i in range(4):
		if board.is_vertex((node[x] + d[i][x], node[y] + d[i][y], node[code])):
			board.add_edge(node, (node[x] + d[i][x], node[y] + d[i][y], node[code]))
for node in board.vertices():
	if dfs_cycle(board, node):
		print('Yes')
		exit()
print('No')


(n, m) = (int(i) for i in input().split())
L = []
visite = [[0 for i in range(m)] for j in range(n)]
for i in range(n):
	L.append(input())

def cycle(i, j, k, x, y):
	if visite[i][j] == 1:
		if k >= 4:
			return True
		else:
			return False
	visite[i][j] = 1
	if i < n - 1 and L[i][j] == L[i + 1][j] and ((i + 1, j) != (x, y)):
		if cycle(i + 1, j, k + 1, i, j):
			return True
	if i > 0 and L[i][j] == L[i - 1][j] and ((i - 1, j) != (x, y)):
		if cycle(i - 1, j, k + 1, i, j):
			return True
	if j > 0 and L[i][j] == L[i][j - 1] and ((i, j - 1) != (x, y)):
		if cycle(i, j - 1, k + 1, i, j):
			return True
	if j < m - 1 and L[i][j] == L[i][j + 1] and ((i, j + 1) != (x, y)):
		if cycle(i, j + 1, k + 1, i, j):
			return True
	return False

def f():
	for i in range(n):
		for j in range(m):
			if visite[i][j] == 0:
				if cycle(i, j, 0, -1, -1):
					print('Yes')
					return
	print('No')
f()


def fastio():
	import sys
	from io import StringIO
	from atexit import register
	global input
	sys.stdin = StringIO(sys.stdin.read())
	input = lambda : sys.stdin.readline().rstrip('\r\n')
	sys.stdout = StringIO()
	register(lambda : sys.__stdout__.write(sys.stdout.getvalue()))
fastio()
import sys
sys.setrecursionlimit(5000)
MOD = 10 ** 9 + 7
I = lambda : list(map(int, input().split()))
(n, m) = I()
g = []
for i in range(n):
	g.append(input())
v = [[0] * m for i in range(n)]

def dfs(x, y, c, pa, py):
	v[x][y] = c
	d = [[-1, 0], [0, -1], [1, 0], [0, 1]]
	for (i, j) in d:
		a = x + i
		b = y + j
		if 0 <= a < n and 0 <= b < m:
			if not v[a][b] and g[a][b] == c:
				dfs(a, b, c, x, y)
			elif v[a][b] == c and a != pa and (b != py):
				print('Yes')
				exit()
c = 1
for i in range(n):
	for j in range(m):
		if not v[i][j]:
			dfs(i, j, g[i][j], -1, -1)
			c += 1
print('No')


from collections import defaultdict
[r, c] = [int(item) for item in input().split(' ')]
graph = [input() for i in range(r)]
visited = defaultdict(lambda : 0)
ID = 1

def legal(coor):
	(x, y) = coor
	return 0 <= x < r and 0 <= y < c

def equal(c1, c2):
	return graph[c1[0]][c1[1]] == graph[c2[0]][c2[1]]

def dfs(i, j):
	stack = [(i, j)]
	p_stack = [(-3, -3)]
	while stack:
		coor = (x, y) = stack.pop()
		p = p_stack.pop()
		visited[coor] = True
		moves = [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]
		for item in moves:
			if legal(item) and equal(coor, item) and (p != item):
				stack.append(item)
				p_stack.append(coor)
				if visited[item]:
					print('Yes')
					exit()
for i in range(r):
	for j in range(c):
		pp = (i, j)
		if not visited[pp]:
			dfs(i, j)
print('No')


import sys
sys.setrecursionlimit(10 ** 5)
(n, m) = list(map(int, input().split()))
cor = [[0 for _ in range(m)] for _ in range(n)]
grid = [list(input()) for _ in range(n)]
g = [[[] for _ in range(m)] for _ in range(n)]
for i in range(n):
	for j in range(m):
		if i and grid[i][j] == grid[i - 1][j]:
			g[i][j].append((i - 1, j))
		if j and grid[i][j] == grid[i][j - 1]:
			g[i][j].append((i, j - 1))
		if i < n - 1 and grid[i][j] == grid[i + 1][j]:
			g[i][j].append((i + 1, j))
		if j < m - 1 and grid[i][j] == grid[i][j + 1]:
			g[i][j].append((i, j + 1))

def achaCic(i, j, pre):
	cor[i][j] = 1
	for v in g[i][j]:
		if v == pre:
			continue
		if cor[v[0]][v[1]] == 0:
			achaCic(v[0], v[1], (i, j))
		if cor[v[0]][v[1]] in [1, 3]:
			cor[i][j] = 3
	if cor[i][j] == 1:
		cor[i][j] = 2
ciclo = 'No'
for i in range(n):
	for j in range(m):
		if cor[i][j] == 0:
			achaCic(i, j, (-1, -1))
			if cor[i][j] == 3:
				ciclo = 'Yes'
print(ciclo)


import sys
sys.setrecursionlimit(10000)

def dfs(x, y, xprev, yprev):
	global vst
	if vst[x][y]:
		return
	vst[x][y] = True
	for (dx, dy) in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
		xprime = x + dx
		yprime = y + dy
		if 0 <= xprime < n and 0 <= yprime < m:
			if color[x][y] == color[xprime][yprime] and (xprime, yprime) != (xprev, yprev):
				if vst[xprime][yprime]:
					print('Yes')
					sys.exit(0)
				else:
					dfs(xprime, yprime, x, y)
(n, m) = tuple(map(int, input().split()))
color = [input() for _ in range(n)]
vst = [[False for _ in range(m)] for _ in range(n)]
for i in range(n):
	for j in range(m):
		dfs(i, j, -1, -1)
print('No')


def is_empty(n, m, data):
	for i in range(n):
		for j in range(m):
			if data[i][j] != 0:
				return False
	return True

def exit(n, m, data):
	flag = True
	for i in range(n):
		for j in range(m):
			if data[i][j] != 0:
				x = 0
				if j + 1 < m and data[i][j] == data[i][j + 1]:
					x += 1
				if j - 1 >= 0 and data[i][j] == data[i][j - 1]:
					x += 1
				if i - 1 >= 0 and data[i][j] == data[i - 1][j]:
					x += 1
				if i + 1 < n and data[i][j] == data[i + 1][j]:
					x += 1
				if x < 2:
					data[i][j] = 0
					flag = False
	if flag:
		if is_empty(n, m, data):
			return False
		else:
			return True
	return exit(n, m, data)
(n, m) = map(int, input().split())
data = []
for i in range(n):
	data.append([ord(c) for c in input()])
print('Yes') if exit(n, m, data) else print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
DIRECTIONS = [[-1, 0], [1, 0], [0, -1], [0, 1]]

def do_dfs(g, cx, cy, ox, oy):
	if g[cx][cy].islower():
		return True
	g[cx][cy] = g[cx][cy].lower()
	for k in range(4):
		x = cx + DIRECTIONS[k][0]
		y = cy + DIRECTIONS[k][1]
		if x < 0 or x >= len(g):
			continue
		if y < 0 or y >= len(g[0]):
			continue
		if x == ox and y == oy:
			continue
		if g[x][y].lower() == g[cx][cy]:
			if do_dfs(g, x, y, cx, cy):
				return True
	return False
(n, m) = map(int, input().split(' '))
g = []
for i in range(n):
	t = []
	for j in input():
		t.append(j)
	g.append(t)
hasCycle = False
for i in range(n):
	if hasCycle:
		break
	for j in range(m):
		if g[i][j].islower():
			continue
		if do_dfs(g, i, j, i, j):
			hasCycle = True
			break
print('Yes' if hasCycle else 'No')


(n, m) = map(int, input().split())
letters = []
for i in range(n):
	word = input()
	letters.append([])
	for letter in word:
		letters[i].append(letter)
for i in range(n):
	for j in range(m):
		x = j + 1
		while x < m and letters[i][j] == letters[i][x]:
			y = i + 1
			while y < n and letters[y][x] == letters[i][j]:
				answer = True
				for k in range(j, x + 1, 1):
					if letters[y][k] != letters[i][j]:
						answer = False
				if answer:
					print('Yes')
					exit()
				y += 1
			x += 1
print('No')


from queue import deque
import sys
sys.setrecursionlimit(1000000)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

class WeightedGraph(Graph):

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = {}

	def add_edge(self, source, destination, weight=None):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source][destination] = weight

	def get_weight(self, source, destination):
		return self._alist[source][destination]

	def neighbours(self, vertex):
		return self._alist[vertex].keys()

	def neighbours_and_weights(self, vertex):
		return self._alist[vertex].items()

def addEdges(g, rawInput, height, width):
	for i in range(height):
		for j in range(width):
			if j != width - 1 and rawInput[i][j] == rawInput[i][j + 1]:
				g.add_edge(i * width + j, i * width + (j + 1))
			if i != height - 1 and rawInput[i][j] == rawInput[i + 1][j]:
				g.add_edge(i * width + j, (i + 1) * width + j)

def findCyclesDFS(g):
	todo = deque(g.vertices())
	reached = {}

	def do_dfs(curr, prev):
		if curr in todo:
			todo.remove(curr)
		if curr in reached:
			return True
		reached[curr] = prev
		for succ in g.neighbours(curr):
			if not succ == prev and do_dfs(succ, curr):
				return True
	while todo:
		reached = {}
		v = todo.popleft()
		if do_dfs(v, v):
			print('Yes')
			return
	print('No')
	return
rawInput = []
(height, width) = input().split(' ')
for i in range(int(height)):
	rawInput.append(input())
ug = UndirectedGraph()
addEdges(ug, rawInput, int(height), int(width))
findCyclesDFS(ug)


import os
import sys
from math import *
from collections import *
from bisect import *
from io import BytesIO, IOBase

def vsInput():
	sys.stdin = open('input.txt', 'r')
	sys.stdout = open('output.txt', 'w')
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def value():
	return tuple(map(int, input().split()))

def array():
	return [int(i) for i in input().split()]

def Int():
	return int(input())

def Str():
	return input()

def arrayS():
	return [i for i in input().split()]

def bfs(adj, s):
	papa = {}
	vis = defaultdict(bool)
	vis[s] = True
	qu = [s]
	while len(qu) != 0:
		e = qu.pop(0)
		for i in adj[e]:
			if vis[i] != True:
				qu.append(i)
				vis[i] = True
				papa[i] = e
			elif papa[e] != i:
				return True
	return False
(r, c) = value()
a = []
adj = defaultdict(list)
for i in range(r):
	a.append(list(input()))
for i in range(r):
	for j in range(c):
		if j < c - 1 and a[i][j] == a[i][j + 1]:
			adj[i, j].append((i, j + 1))
			adj[i, j + 1].append((i, j))
		if i < r - 1 and a[i][j] == a[i + 1][j]:
			adj[i, j].append((i + 1, j))
			adj[i + 1, j].append((i, j))
for i in range(r):
	for j in range(c):
		if bfs(adj, (i, j)):
			print('Yes')
			exit()
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 sys
sys.setrecursionlimit(10000)
(n, m) = 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):
	global 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')
				sys.exit(0)
print('No')


(row, col) = map(int, input().split())
chart = []
trOne = 0
trTwo = 0
for _ in range(row):
	rowInput = input()
	oneRow = []
	if col == len(rowInput):
		for i in rowInput:
			oneRow.append(i)
		chart.append(oneRow)
	else:
		print('Row input error')
		exit()
while True:
	for i in range(row):
		for j in range(col):
			sum = 0
			if chart[i][j] != ' ':
				if i > 0 and chart[i - 1][j] == chart[i][j]:
					sum += 1
				if i < row - 1 and chart[i + 1][j] == chart[i][j]:
					sum += 1
				if j > 0 and chart[i][j - 1] == chart[i][j]:
					sum += 1
				if j < col - 1 and chart[i][j + 1] == chart[i][j]:
					sum += 1
				if sum < 2:
					chart[i][j] = ' '
					trOne += 1
	if trOne == row * col:
		print('No')
		break
	elif trOne == trTwo:
		print('Yes')
		break
	else:
		trTwo = trOne


import sys
sys.setrecursionlimit(10000)

def is_cycle(x, y, x1, y1):
	seen.add((x, y))
	for (dx, dy) in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
		if 0 <= x + dx < n and 0 <= y + dy < m and ((x + dx, y + dy) != (x1, y1)) and (dots[x + dx][y + dy] == dots[x][y]):
			if (x + dx, y + dy) in seen or is_cycle(x + dx, y + dy, x, y):
				return True
	return False
(n, m) = map(int, input().split())
dots = [input() for i in range(n)]
seen = set()
for i in range(n):
	for j in range(m):
		if not (i, j) in seen and is_cycle(i, j, -1, -1):
			print('Yes')
			exit()
print('No')


(n, m) = map(int, input().split())
visited = [[False] * m for _ in range(n)]
G = []
for _ in range(n):
	G.append(list(input()))

def dfs(x, y, c, lx, ly):
	if x < 0 or x >= n or y < 0 or (y >= m) or (G[x][y] != c):
		return False
	if visited[x][y]:
		return True
	visited[x][y] = True
	for (i, j) in ((0, 1), (0, -1), (-1, 0), (1, 0)):
		(nx, ny) = (x + i, y + j)
		if nx != lx or ny != ly:
			if dfs(nx, ny, c, x, y):
				return True
	return False

def judge():
	for i in range(n):
		for j in range(m):
			if not visited[i][j]:
				if dfs(i, j, G[i][j], -1, -1):
					return True
	return False
if judge():
	print('Yes')
else:
	print('No')


import sys
sys.setrecursionlimit(10 ** 5)
(n, m) = map(int, input().split())
grid = []
cor = []
for p in range(n):
	cor.append([0] * m)
g = []
for q in range(n):
	g.append([])
	for r in range(m):
		g[q].append([])
for i in range(n):
	linha = input()
	grid.append(linha)
for i in range(n):
	for j in range(m):
		if i and grid[i][j] == grid[i - 1][j]:
			g[i][j].append((i - 1, j))
		if j and grid[i][j] == grid[i][j - 1]:
			g[i][j].append((i, j - 1))
		if i < n - 1 and grid[i][j] == grid[i + 1][j]:
			g[i][j].append((i + 1, j))
		if j < m - 1 and grid[i][j] == grid[i][j + 1]:
			g[i][j].append((i, j + 1))

def achaCiclo(i, j, pre):
	cor[i][j] = 1
	for v in g[i][j]:
		if v == pre:
			continue
		if cor[v[0]][v[1]] == 0:
			achaCiclo(v[0], v[1], (i, j))
		if cor[v[0]][v[1]] in [1, 3]:
			cor[i][j] = 3
	if cor[i][j] == 1:
		cor[i][j] = 2
ciclo = 'No'
for i in range(n):
	for j in range(m):
		if cor[i][j] == 0:
			achaCiclo(i, j, (-1, -1))
			if cor[i][j] == 3:
				ciclo = 'Yes'
print(ciclo)


(n, m) = map(int, input().split())
arr = [list(input()) for row in range(n)]
marked = [[False for col in range(m)] for row in range(n)]
visited = []
parent = {}
for i in range(n):
	for j in range(m):
		if not marked[i][j]:
			visited.append((i, j))
			parent[i, j] = None
			marked[i][j] = True
			while visited:
				(row, col) = visited.pop()
				letter = arr[row][col]
				neighbors = []
				if row - 1 >= 0 and arr[row - 1][col] == letter:
					neighbors.append((row - 1, col))
				if row + 1 <= n - 1 and arr[row + 1][col] == letter:
					neighbors.append((row + 1, col))
				if col - 1 >= 0 and arr[row][col - 1] == letter:
					neighbors.append((row, col - 1))
				if col + 1 <= m - 1 and arr[row][col + 1] == letter:
					neighbors.append((row, col + 1))
				for neighbor in neighbors:
					(new_row, new_col) = neighbor
					if neighbor != parent[row, col]:
						parent[neighbor] = (row, col)
						if marked[new_row][new_col]:
							print('Yes')
							exit()
						visited.append(neighbor)
						marked[new_row][new_col] = True
print('No')


(n, m) = map(int, input().split())
arr = []
for _ in range(n):
	arr.append(list(input()))
ch = '0'

def dfs(i, j, fa, fj):
	if i < 0 or i >= n or j < 0 or (j >= m):
		return False
	if arr[i][j] == 0:
		return True
	if arr[i][j] == 1:
		return False
	if arr[i][j] == ch:
		arr[i][j] = 0
		for (nx, ny) in [[i - 1, j], [i + 1, j], [i, j - 1], [i, j + 1]]:
			if (nx, ny) != (fa, fj) and dfs(nx, ny, i, j):
				return True
		arr[i][j] = 1
		return False
	return False
for i in range(n):
	for j in range(m):
		if arr[i][j] != 0 and arr[i][j] != 1:
			ch = arr[i][j]
			if dfs(i, j, -1, -1):
				print('Yes')
				exit()
print('No')


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):
	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
		if L[x][y] == L[newx][newy]:
			if visited[newx][newy]:
				return True
			if DFS(newx, newy, x, y, visited):
				return True
	return False

def solve():
	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')


import sys
sys.setrecursionlimit(2500)
matrix = []

def sameClass(elem, i, j):
	if i >= 0 and i < n and (j >= 0) and (j < m):
		if elem == matrix[i][j]:
			return True
	return False

def neighbors(i, j):
	result = []
	neigh = [(i + 1, j), (i - 1, j), (i, j - 1), (i, j + 1)]
	for (k, l) in neigh:
		if sameClass(matrix[i][j], k, l):
			result.append((k, l))
	return result

def loadGraph():
	colors = {}
	for i in range(n):
		for j in range(m):
			if matrix[i][j] not in colors:
				colors[matrix[i][j]] = {(i, j): []}
			colors[matrix[i][j]][i, j] = neighbors(i, j)
	return colors

def haveCycle(color, current, previous=(-1, -1)):
	visited[current] = True
	for neighbor in color[current]:
		if visited[neighbor] == False:
			if haveCycle(color, neighbor, current):
				return True
		elif neighbor != previous:
			return True
	return False

def hasUnvisited(visited):
	for (key, vis) in visited.items():
		if vis == False:
			return key
	return None
(n, m) = [int(x) for x in input().split()]
for i in range(n):
	matrix.append(list(input()))
colors = loadGraph()
for (key, color) in colors.items():
	visited = dict.fromkeys(color.keys(), False)
	if len(visited) > 3:
		current = hasUnvisited(visited)
		while current is not None:
			if haveCycle(color, current):
				print('Yes')
				exit()
			current = hasUnvisited(visited)
print('No')


import math
import time
from collections import defaultdict, deque
from sys import stdin, stdout
from bisect import bisect_left, bisect_right
import sys

class Graph:

	def __init__(self, n):
		sys.setrecursionlimit(100000)
		self.graph = defaultdict(lambda : [])
		self.vertices = n

	def addedge(self, a, b):
		self.graph[a].append(b)

	def topologial_sortUntill(self, curr, visited, ans):
		visited[curr] = True
		for i in self.graph[curr]:
			if not visited[i]:
				self.topologial_sortUntill(i, visited, ans)
		ans.appendleft(curr)

	def topologial_sort(self):
		n = self.vertices
		visited = [False] * (n + 2)
		ans = deque()
		for i in range(1, n + 1):
			if not visited[i]:
				self.topologial_sortUntill(i, visited, ans)
		return ans

	def cycleUntill(self, visited, curr, parent):
		visited[curr] = True
		for i in self.graph[curr]:
			if not visited[i]:
				if self.cycleUntill(visited, i, curr):
					return True
			elif i != parent:
				return True
		return False

	def cycle(self):
		n = self.vertices
		visited = [False] * (n + 2)
		for i in range(1, n + 1):
			if not visited[i]:
				if self.cycleUntill(visited, i, -1):
					return True
		return False

	def directed_cycleUntill(self, curr, visited, recstack):
		visited[curr] = True
		recstack[curr] = True
		for i in self.graph[curr]:
			if not visited[i]:
				if self.directed_cycleUntill(i, visited, recstack):
					return True
			elif recstack[i]:
				return True
		recstack[curr] = False
		return False

	def directed_cycle(self):
		n = self.vertices
		visited = [False] * (n + 2)
		recstack = [False] * (n + 2)
		for i in range(1, n + 1):
			if not visited[i]:
				if self.directed_cycleUntill(i, visited, recstack):
					return True
			return False
(n, m) = map(int, stdin.readline().split())
grid = []
for _ in range(n):
	grid.append(stdin.readline())
g = Graph(n * m)
for i in range(n):
	for j in range(m):
		if i < n - 1 and grid[i][j] == grid[i + 1][j]:
			g.addedge(i * m + j + 1, (i + 1) * m + j + 1)
			g.addedge((i + 1) * m + j + 1, i * m + j + 1)
		if j < m - 1 and grid[i][j] == grid[i][j + 1]:
			g.addedge(i * m + j + 1, i * m + j + 2)
			g.addedge(i * m + j + 2, i * m + j + 1)
if g.cycle():
	print('Yes')
else:
	print('No')


from collections import deque, namedtuple
P = namedtuple('P', ['ff', 'ss', 'parent'])
dx = [0, 0, -1, 1]
dy = [1, -1, 0, 0]

def found(a, visited, n, m, sx, sy):
	(dq, doing) = (deque(), a[sx][sy])
	dq.append(P(sx, sy, (-1, -1)))
	while len(dq) > 0:
		now = dq.pop()
		visited[now.ff][now.ss] = 1
		for i in range(4):
			(tx, ty) = (dx[i] + now.ff, dy[i] + now.ss)
			if not (0 <= tx < n and 0 <= ty < m and (a[tx][ty] == doing)):
				continue
			if visited[tx][ty]:
				if not (now.parent[0] == tx and now.parent[1] == ty):
					return True
			else:
				dq.append(P(tx, ty, (now.ff, now.ss)))

def main():
	(n, m) = map(int, input().split())
	a = [input() for i in range(n)]
	visited = [[0 for i in range(m)] for j in range(n)]
	for i in range(n):
		for j in range(m):
			if visited[i][j]:
				continue
			if found(a, visited, n, m, i, j):
				print('Yes')
				exit(0)
	print('No')
main()


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached
rowscols = input().split()
rows = int(rowscols[0])
cols = int(rowscols[1])
g = UndirectedGraph()
colours = {}
name = 0
for i in range(rows):
	r = input()
	for e in range(cols):
		g.add_vertex(name)
		colours[name] = r[e]
		name += 1
name = 0
for i in range(rows):
	for e in range(cols - 1):
		n_c = name
		n_n = name + 1
		if colours[n_c] == colours[n_n]:
			g.add_edge(n_c, n_n)
		name += 1
	name += 1
name = 0
for e in range(cols):
	for i in range(rows - 1):
		n_c = name
		n_n = name + cols
		if colours[n_c] == colours[n_n]:
			g.add_edge(n_c, n_n)
		name += 1

def is_cycle(g):
	for v in g.vertices():
		searched = depth_first_search(g, v)
		for i in g.neighbours(v):
			if searched[i] != v:
				print('Yes')
				return
	print('No')
is_cycle(g)


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):
	global ln
	global cl
	while True:
		if len(path) == 0:
			break
		cur_x = path[-1][0]
		cur_y = path[-1][1]
		del path[-1]
		neighbours = neighb(cur_x, cur_y, ln, cl, lst)
		if len(neighbours) != 0:
			for neighbour in neighbours:
				if neighbour in path:
					return True
				if ch_dt[neighbour[0]][neighbour[1]] == 0:
					path.append(neighbour)
					ch_dt[neighbour[0]][neighbour[1]] = 1
	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')
				exit(0)
print('No')


(n, m) = map(int, input().split())
b = []
for i in range(n):
	b.append([i for i in input()])
fc = False
for _ in range(26):
	v = [[0] * m for i in range(n)]
	char = chr(65 + _)
	ct = 1
	if fc:
		break
	while True:
		found = False
		q = []
		for i in range(n):
			for j in range(m):
				if b[i][j] == char and (not v[i][j]):
					q.append((i, j))
					found = True
					break
			if found:
				break
		if not found:
			break
		while q:
			(i, j) = q.pop()
			if v[i][j] == ct:
				fc = True
				break
			v[i][j] = ct
			if i != 0:
				if b[i - 1][j] == char and v[i - 1][j] != ct:
					q.append((i - 1, j))
			if i != n - 1:
				if b[i + 1][j] == char and v[i + 1][j] != ct:
					q.append((i + 1, j))
			if j != 0:
				if b[i][j - 1] == char and v[i][j - 1] != ct:
					q.append((i, j - 1))
			if j != m - 1:
				if b[i][j + 1] == char and v[i][j + 1] != ct:
					q.append((i, j + 1))
		ct += 1
print('Yes' if fc else 'No')


import sys
sys.setrecursionlimit(10 ** 5)
(n, m) = [int(x) for x in input().split()]
tudo = []
g = []
cor = []
ciclo = ['No']
for e in range(n):
	entrada = input()
	tudo.append(entrada)
	g.append([[]] * m)
	cor.append([False] * m)

def gera_lista_adjacencia():
	for i in range(n):
		for j in range(m):
			aux = []
			if i and tudo[i][j] == tudo[i - 1][j]:
				aux.append((i - 1, j))
			if j and tudo[i][j] == tudo[i][j - 1]:
				aux.append((i, j - 1))
			if i + 1 < n and tudo[i][j] == tudo[i + 1][j]:
				aux.append((i + 1, j))
			if j + 1 < m and tudo[i][j] == tudo[i][j + 1]:
				aux.append((i, j + 1))
			g[i][j] = aux

def dfs(anterior, i, j):
	cor[i][j] = True
	for e in g[i][j]:
		if e == anterior:
			continue
		if cor[e[0]][e[1]]:
			ciclo[0] = 'Yes'
		elif not cor[e[0]][e[1]]:
			dfs((i, j), e[0], e[1])
gera_lista_adjacencia()
for i in range(n):
	for j in range(m):
		if not cor[i][j]:
			dfs((-1, -1), i, j)
print(ciclo[0])


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)


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
mk_ver = {}
Detector_cycle = False

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached

def D_F_S(g, curr, prev):
	global Detector_cycle
	global mk_ver
	if Detector_cycle:
		return
	mk_ver[curr] = True
	for succ in g.neighbours(curr):
		if mk_ver[succ] and succ != prev:
			Detector_cycle = True
			return
		if not mk_ver[succ]:
			D_F_S(g, succ, curr)

def cycleExists(g):
	global Detector_cycle
	global mk_ver
	for v in g.vertices():
		mk_ver[v] = False
	for v in g.vertices():
		if not mk_ver[v]:
			D_F_S(g, v, v)
		if Detector_cycle:
			break
	return
(row_Number, column_Number) = map(int, input().split(' '))
rows = []
for i in range(row_Number):
	rows.append(input())
node_dict = {}
for a in range(row_Number):
	for b in range(column_Number):
		node_dict[a * column_Number + b] = rows[a][b]
two_dots = UndirectedGraph()
for v in range(row_Number * column_Number):
	two_dots.add_vertex(v)
	try:
		if node_dict[v] == node_dict[v + 1] and v % column_Number != column_Number - 1:
			two_dots.add_edge(v, v + 1)
		if node_dict[v] == node_dict[v + column_Number]:
			two_dots.add_edge(v, v + column_Number)
	except:
		continue
cycleExists(two_dots)
if Detector_cycle:
	print('Yes')
else:
	print('No')


from queue import deque
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def contains_cycle(g):
	parent = {}
	global cycle
	cycle = False

	def do_dfs(curr, prev):
		global cycle
		if cycle:
			return
		if curr in parent:
			return
		parent[curr] = prev
		for succ in g.neighbours(curr):
			if succ == parent[curr]:
				continue
			if succ in parent:
				cycle = True
				return
			do_dfs(succ, curr)
	for v in g.vertices():
		if v not in parent:
			do_dfs(v, None)
			if cycle:
				break
	return cycle
(n, m) = map(int, input().split())
colourGraphs = {}
for currentRow in range(n):
	currentLine = input()
	for x in range(m):
		currentIndex = currentRow * m + x
		c = currentLine[x]
		if c not in colourGraphs:
			colourGraphs[c] = UndirectedGraph()
		colourGraphs[c].add_vertex(currentIndex)
		if colourGraphs[c].is_vertex(currentIndex - 1) and x != 0:
			colourGraphs[c].add_edge(currentIndex, currentIndex - 1)
		if currentIndex >= m and colourGraphs[c].is_vertex(currentIndex - m):
			colourGraphs[c].add_edge(currentIndex, currentIndex - m)
foundCycle = False
for c in colourGraphs:
	if contains_cycle(colourGraphs[c]):
		foundCycle = True
		break
if foundCycle:
	print('Yes')
else:
	print('No')


import sys
import traceback

def log(data, file=sys.stdout):
	sf = traceback.extract_stack()[-2]
	print('\nIn {} on line {} in {}\n    {}'.format(sf[0], sf[1], sf[2], data, file=file))

def read_config(line):
	return [int(i) for i in line.strip().split()]

def read_data(num_rows, get_line=input):
	return [list(get_line().strip()) for _ in range(num_rows)]

def connect_nodes_in_row(data, first_id):
	edges = set()
	if len(data) > 1:
		i = 0
		if data[i] == data[i + 1]:
			edges.add((first_id + i, first_id + i + 1))
			edges.add((first_id + i + 1, first_id + i))
		for i in range(1, len(data) - 1):
			if data[i] == data[i + 1]:
				edges.add((first_id + i, first_id + i + 1))
				edges.add((first_id + i + 1, first_id + i))
	return edges

class Graph:

	@classmethod
	def from_data(self, data):
		i = 0
		edges = set()
		previous_row = data[0]
		new_edges = connect_nodes_in_row(previous_row, i)
		edges.update(new_edges)
		i += len(previous_row)
		for row_index in range(1, len(data)):
			row = data[row_index]
			edges.update(connect_nodes_in_row(row, i))
			for column in range(len(row)):
				if column < len(previous_row) and row[column] == previous_row[column]:
					edges.add((i + column, i + column - len(row)))
					edges.add((i + column - len(row), i + column))
			previous_row = row
			i += len(row)
		return self(set(range(i)), edges)

	def __init__(self, vertices, edges):
		self.vertices_to_edges = {}
		for v in vertices:
			self.vertices_to_edges[v] = set()
		for e in edges:
			self.vertices_to_edges[e[0]].add(e[1])

	def neighbors(self, v):
		return self.vertices_to_edges[v]

	def contains_cycle(self):
		self.vertices = set(self.vertices_to_edges.keys())
		while self.vertices:
			v = self.vertices.pop()
			if self.subgraph_root_at_v_contains_cycle(v):
				return True
			self.vertices.remove(v)

	def subgraph_root_at_v_contains_cycle(self, v):
		current = v
		encounted_nodes = set([v])
		nodes_to_traverse = [v]
		while nodes_to_traverse:
			n = nodes_to_traverse.pop()
			if n in encounted_nodes:
				return True
			else:
				encounted_nodes.add(n)
				nodes_to_traverse += list(self.neighbors(n))

	def contains_box(self):
		self.vertices = set(self.vertices_to_edges.keys())
		while self.vertices:
			v = self.vertices.pop()
			if self.subgraph_root_at_v_contains_box(v):
				return True
		return False

	def subgraph_root_at_v_contains_box(self, v):
		encounted_nodes = []
		encounted_nodes_set = set()
		nodes_to_traverse = [(v, 0)]
		previous_length = -1
		while nodes_to_traverse:
			(n, length) = nodes_to_traverse.pop()
			if previous_length > length:
				for _ in range(length - previous_length):
					encounted_nodes_set.remove(encounted_nodes.pop())
			if n in encounted_nodes_set:
				if n == v and length > 3:
					return True
				previous_length = length
			else:
				encounted_nodes.append(n)
				encounted_nodes_set.add(n)
				for n_prime in self.neighbors(n):
					nodes_to_traverse.append((n_prime, length + 1))
		return False

def contains_box(get_line=input):
	return Graph.from_data(read_data(read_config(get_line())[0], get_line)).contains_box()

def solve_510B(get_line=input):
	if contains_box(get_line):
		return 'Yes'
	else:
		return 'No'
print(solve_510B())


import sys
sys.setrecursionlimit(10000)
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]

def is_valid(x, y):
	return x < rows and y < len(points[x]) and (x >= 0) and (y >= 0)

def dfs(x, y, fx, fy):
	if visited[x][y]:
		print('Yes')
		exit()
	visited[x][y] = True
	for i in range(4):
		next_x = x + directions[i][0]
		next_y = y + directions[i][1]
		if next_x == fx and next_y == fy:
			continue
		if is_valid(x, y) and is_valid(next_x, next_y):
			currPoint = points[x][y]
			nextPoint = points[next_x][next_y]
			if currPoint == nextPoint:
				dfs(next_x, next_y, x, y)
(rows, cols) = map(int, input().split())
visited = [[False for x in range(cols)] for x in range(rows)]
points = [['' for x in range(cols)] for x in range(rows)]
for i in range(rows):
	points[i] = list(input())
for i in range(rows):
	for j in range(cols):
		if not visited[i][j]:
			dfs(i, j, i, j)
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()


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):
	global 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')


from sys import setrecursionlimit
setrecursionlimit(1000000)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached

def check_cycle(g):
	path_exists = 0
	for j in g.vertices():
		path = depth_first_search(g, j)
		len_path = len(path)
		if len_path < 4:
			continue
		elif len_path >= 4:
			len_nei = len(g.neighbours(j))
			for k in range(0, len_nei):
				x = g.neighbours(j)
				x_prime = x.pop()
				path_1 = path[x_prime]
				if path_1 != j:
					path_exists = 1
				range_path = len_path % 2
				if range_path == 0 and path_exists == 1:
					print('Yes')
					return
				elif range_path == 1:
					print('No')
					return
	print('No')
n = input()
n = n.split(' ')
y = int(n[1])
x = int(n[0])
data = dict()
string_char = []
g = UndirectedGraph()
for k in range(0, x):
	string_char.append(input())
nodes = 0
for i in range(0, x):
	for j in range(0, y):
		g.add_vertex(nodes)
		data[nodes] = string_char[i][j]
		nodes = nodes + 1
counter = 0
max_len = 0
for i in range(0, len(data) - 1):
	value = counter
	value_p1 = counter + 1
	if max_len == y - 1:
		max_len = 0
		counter = counter + 1
	elif data[value] == data[value_p1]:
		g.add_edge(value, value_p1)
		counter = counter + 1
		max_len = max_len + 1
	else:
		counter = counter + 1
		max_len = max_len + 1
counter = 0
value = 0
for j in range(0, len(data) - y):
	value = counter
	value_prime = counter + y
	if data[value] == data[value_prime]:
		g.add_edge(value, value_prime)
		counter = counter + 1
	else:
		counter = counter + 1
check_cycle(g)


from sys import setrecursionlimit

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def has_cycle(graph, start):
	reached = {}

	def find_cycle(first, second, third, fourth):
		if first not in reached:
			reached[first] = second
			for next in graph.neighbours(first):
				if find_cycle(next, first, second, third):
					return True
		elif first not in {second, third, fourth}:
			return True
	return find_cycle(start, start, start, start)
setrecursionlimit(10 ** 6)
graph = UndirectedGraph()
letters = []
(rows, columns) = [int(i) for i in input().split()]
for row in range(rows):
	letters.append([])
	for (column, letter) in enumerate(input()):
		graph.add_vertex((row, column))
		letters[row].append(letter)
		if column > 0 and letters[row][column - 1] == letter:
			graph.add_edge((row, column), (row, column - 1))
		if row > 0 and letters[row - 1][column] == letter:
			graph.add_edge((row, column), (row - 1, column))
found_cycle = False
for x in range(rows):
	for y in range(columns):
		if has_cycle(graph, (x, y)):
			found_cycle = True
			break
	if found_cycle:
		break
print('Yes' if found_cycle else 'No')


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def neighbours(self, vertex):
		return self._alist[vertex]

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached
from sys import setrecursionlimit
setrecursionlimit(10000)
(n, m) = map(int, input().split())
g = UndirectedGraph()
matrix = []
for i in range(n):
	matrix.append(input())
reachable = {}
for a in range(n):
	for b in range(m):
		g.add_vertex((a, b))
		reachable[a, b] = matrix[a][b]
		if b > 0:
			if reachable[a, b - 1] is matrix[a][b]:
				g.add_edge((a, b - 1), (a, b))
		if b < m - 1:
			if reachable[a, b] is matrix[a][b]:
				g.add_edge((a, b), (a, b))
		if a > 0:
			if reachable[a - 1, b] is matrix[a][b]:
				g.add_edge((a - 1, b), (a, b))
		if a < n - 1:
			if reachable[a, b] is matrix[a][b]:
				g.add_edge((a, b), (a, b))
for a in range(n):
	for b in range(m):
		vertex = (a, b)
		reached = depth_first_search(g, vertex)
		for i in g.neighbours(vertex):
			if reached[i] is not vertex:
				print('Yes')
				exit()
print('No')


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self._alist[source].add(destination)

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search_for_cycle(g):

	def do_dfs(curr, prev):
		global cycle
		if cycle:
			return
		if curr in reached:
			return
		reached.add(curr)
		for succ in g.neighbours(curr):
			if succ == prev:
				continue
			if succ in reached:
				cycle = True
				return
			do_dfs(succ, curr)
	reached = set()
	global cycle
	cycle = False
	for v in g.vertices():
		if v not in reached:
			do_dfs(v, v)
		if cycle:
			print('Yes')
			exit()
	print('No')
from sys import setrecursionlimit
setrecursionlimit(2500)
(rows, columns) = map(int, input().split(' '))
g = UndirectedGraph()
vertices = dict()
for r in range(rows):
	row = input()
	for c in range(columns):
		g.add_vertex((r, c))
		vertices[r, c] = row[c]
		if c > 0 and vertices[r, c - 1] is row[c]:
			g.add_edge((r, c - 1), (r, c))
		if r > 0 and vertices[r - 1, c] is row[c]:
			g.add_edge((r - 1, c), (r, c))
depth_first_search_for_cycle(g)


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def iscyclic(g):
	todo = []
	V = g.vertices()
	layer = {v: -1 for v in V}
	for v in V:
		if layer[v] != -1:
			continue
		layer[v] = 0
		todo.append(v)
		while todo:
			curr = todo.pop()
			succ = g.neighbours(curr)
			for i in succ:
				if layer[i] == -1:
					layer[i] = layer[curr] + 1
					todo.append(i)
				elif layer[i] >= layer[curr]:
					return True
	return False
twonum = input().split()
row = int(twonum[0])
col = int(twonum[1])
board = list()
colors = set()
for i in range(int(row)):
	line = input()
	for j in range(int(col)):
		board.append([line[j], j, i])
		if line[j] not in colors:
			colors.add(line[j])
for x in colors:
	g = UndirectedGraph()
	for y in range(len(board)):
		if board[y][0] == x:
			g.add_vertex(str(board[y]))
			if board[y][1] > 0 and board[y - 1][0] == x:
				g.add_edge(str(board[y]), str(board[y - 1]))
			if board[y][2] > 0 and board[y - col][0] == x:
				g.add_edge(str(board[y]), str(board[y - col]))
	if len(g.vertices()) < 4:
		continue
	if iscyclic(g):
		print('Yes')
		break
else:
	print('No')


import sys
(n, m) = input().split(' ')
n = int(n)
m = int(m)
key_list = list()
for i in range(n):
	readline = input()
	listline = list(readline)
	key_list.extend(listline)

def neighbours(vertex):
	output_list = list()
	row_num = (vertex + 1) % m
	if row_num != 1:
		if key_list[vertex] == key_list[vertex - 1]:
			output_list.append(vertex - 1)
	if row_num != 0:
		if key_list[vertex] == key_list[vertex + 1]:
			output_list.append(vertex + 1)
	if vertex + m < len(key_list):
		if key_list[vertex] == key_list[vertex + m]:
			output_list.append(vertex + m)
	if vertex - m >= 0:
		if key_list[vertex] == key_list[vertex - m]:
			output_list.append(vertex - m)
	return output_list
already_read = set()
print_massage = 'No'
for j in range(len(key_list)):
	if print_massage == 'Yes':
		break
	if len(neighbours(j)) < 2:
		already_read.add(j)
	if j not in already_read:
		reached = {j: j}
		todo = {j}
		while todo:
			if print_massage == 'Yes':
				break
			curr = todo.pop()
			if curr not in already_read:
				already_read.add(curr)
			for succ in neighbours(curr):
				if succ in reached:
					if succ != reached[curr]:
						print_massage = 'Yes'
						break
				if succ not in reached:
					reached[succ] = curr
					todo.add(succ)
print(print_massage)


(n, m) = map(int, input().split())
palavras = [[] for i in range(n)]
for i in range(n):
	palavra = input()
	for letra in palavra:
		palavras[i].append(letra)
for i in range(n):
	for j in range(m):
		x = j + 1
		while x < m and palavras[i][j] == palavras[i][x]:
			y = i + 1
			while y < n and palavras[y][x] == palavras[i][j]:
				troca = True
				for k in range(j, x + 1, 1):
					if palavras[y][k] != palavras[i][j]:
						troca = False
				if troca:
					print('Yes')
					exit()
				y += 1
			x += 1
print('No')


(n, m) = list(map(int, input().split()))
matrix = []
loop_matrix = []
for i in range(n):
	loop_matrix.append([0] * m)
for i in range(n):
	matrix.append(input())

def is_diff(i, j, pi, pj):
	return i != pi or j != pj

def has_loop(i, j, pi, pj):
	if loop_matrix[i][j] != 0:
		return False
	loop_matrix[i][j] = 1
	if i - 1 >= 0 and is_diff(i - 1, j, pi, pj) and (matrix[i][j] == matrix[i - 1][j]) and (loop_matrix[i - 1][j] == 1 or has_loop(i - 1, j, i, j)):
		return True
	if j - 1 >= 0 and is_diff(i, j - 1, pi, pj) and (matrix[i][j] == matrix[i][j - 1]) and (loop_matrix[i][j - 1] == 1 or has_loop(i, j - 1, i, j)):
		return True
	if i + 1 < n and is_diff(i + 1, j, pi, pj) and (matrix[i][j] == matrix[i + 1][j]) and (loop_matrix[i + 1][j] == 1 or has_loop(i + 1, j, i, j)):
		return True
	if j + 1 < m and is_diff(i, j + 1, pi, pj) and (matrix[i][j] == matrix[i][j + 1]) and (loop_matrix[i][j + 1] == 1 or has_loop(i, j + 1, i, j)):
		return True
	return False
has_some_loop = False
for i in range(n):
	for j in range(m):
		if has_loop(i, j, 0, 0):
			has_some_loop = True
			break
if has_some_loop:
	print('Yes')
else:
	print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def my_depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			print('Yes')
			quit()
		reached[curr] = prev
		for succ in g.neighbours(curr):
			if succ != prev:
				do_dfs(succ, curr)
		return False
	do_dfs(v, v)
	return reached
rowsAndColsInput = input().split()
numRows = int(rowsAndColsInput[0])
numCols = int(rowsAndColsInput[1])
numNodes = numRows * numCols

def create_board_single():
	board = list()
	for i in range(numRows):
		board.extend(input())
	g = UndirectedGraph()
	for vertex in range(numNodes):
		g.add_vertex(vertex)
	for i in range(numCols):
		for j in range(numRows):
			if i < numCols - 1:
				currIdx = i + j * numCols
				nextIdxH = i + j * numCols + 1
				if board[currIdx] == board[nextIdxH]:
					g.add_edge(currIdx, nextIdxH)
			if j < numRows - 1:
				currIdx = i + j * numCols
				nextIdxV = i + (j + 1) * numCols
				if board[currIdx] == board[nextIdxV]:
					g.add_edge(currIdx, nextIdxV)
	return g

def is_cycle(g):
	myVertices = list(g.vertices())
	myVerticesLen = len(myVertices)
	visited = set()
	for i in range(myVerticesLen):
		if myVertices[i] not in visited:
			bfs = my_depth_first_search(g, myVertices[i])
			keys = list(bfs.keys())
			visited.update(keys)
is_cycle(create_board_single())
print('No')


from sys import setrecursionlimit, stdin
setrecursionlimit(3000)

def go(cur, prev, used):
	x = cur[0]
	y = cur[1]
	if used[x][y]:
		return True
	used[x][y] = 1
	for (dx, dy) in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
		nx = x + dx
		ny = y + dy
		if nx < 0 or ny < 0 or nx >= n or (ny >= m):
			continue
		if field[x][y] != field[nx][ny]:
			continue
		if (nx, ny) == prev:
			continue
		if go((nx, ny), cur, used):
			return True
	return False
(n, m) = (int(x) for x in stdin.readline().split())
field = stdin.readlines()
used = [[0] * m for _ in range(n)]
for i in range(n):
	for j in range(m):
		if not used[i][j] and go((i, j), (-1, -1), used):
			print('Yes')
			exit()
print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
ok = False
vis = []
mat = []
opx = [0, 0, 1, -1]
opy = [1, -1, 0, 0]

def dfs(i, j, x, y, row, col):
	global vis
	global mat
	global ok
	if ok:
		return
	vis[i * col + j] = True
	for z in range(4):
		nx = i + opx[z]
		ny = j + opy[z]
		if nx < 0 or nx >= row or ny < 0 or (ny >= col):
			continue
		if mat[i * col + j] == mat[nx * col + ny]:
			if vis[nx * col + ny] and (nx != x or ny != y):
				ok = True
			elif not vis[nx * col + ny] and mat[i * col + j] == mat[nx * col + ny]:
				dfs(nx, ny, i, j, row, col)

def main():
	global vis
	global mat
	global ok
	while True:
		line_input = input()
		if line_input == '':
			break
		(row, col) = line_input.strip().split(' ')
		row = int(row)
		col = int(col)
		mat = []
		for i in range(row):
			row_input = input()
			for item in row_input.strip():
				mat.append(item)
		vis = [False] * (row * col)
		ok = False
		for i in range(row):
			if ok:
				break
			for j in range(col):
				if not vis[i * col + j]:
					dfs(i, j, -1, -1, row, col)
		if ok:
			print('Yes')
		else:
			print('No')
		break
main()


v = 0
par = []

def init(siz):
	v = siz
	par.clear()
	for i in range(v):
		par.append(-1)
	return

def root(x):
	if par[x] < 0:
		return x
	r = root(par[x])
	par[x] = r
	return r

def size(x):
	return -par[root(x)]

def same(x, y):
	return root(x) == root(y)

def unite(x, y):
	rx = root(x)
	ry = root(y)
	if rx == ry:
		return False
	if par[rx] > par[ry]:
		rx ^= ry
		ry ^= rx
		rx ^= ry
	par[rx] += par[ry]
	par[ry] = rx
	return True
(n, m) = map(int, input().split())
s = [list(input()) for _ in range(n)]
ans = 'No'
init(n * m)
for i in range(n):
	for j in range(m):
		if i < n - 1:
			if s[i][j] == s[i + 1][j]:
				if not unite(i * m + j, (i + 1) * m + j):
					ans = 'Yes'
		if j < m - 1:
			if s[i][j] == s[i][j + 1]:
				if not unite(i * m + j, i * m + j + 1):
					ans = 'Yes'
print(ans)


import sys
sys.setrecursionlimit(10000)
f = lambda : input()
(n, m) = 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.setrecursionlimit(1000000)

def search(x, y, px=-1, py=-1):
	game[x][y] = True
	dx = [-1, 1, 0, 0]
	dy = [0, 0, -1, 1]
	for i in range(4):
		nx = x + dx[i]
		ny = y + dy[i]
		if (0 <= nx < n and 0 <= ny < m) and (nx != px or ny != py) and (dot[x][y] == dot[nx][ny]):
			if game[nx][ny]:
				print('Yes')
				exit()
			else:
				search(nx, ny, x, y)
(n, m) = [int(i) for i in input().split()]
dot = []
game = [[False] * m for i in range(n)]
for i in range(n):
	dot.append(input())
for i in range(n):
	for j in range(m):
		if not game[i][j]:
			search(i, j)
print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def has_cycle(graph):

	def has_cycle_helper(graph, v, reached, prev):
		reached[v] = True
		for n in graph.neighbours(v):
			if not reached[n]:
				if has_cycle_helper(graph, n, reached, v):
					return True
			elif n != prev:
				return True
		return False
	reached = []
	for i in range(len(graph.vertices())):
		reached.append(False)
	for j in range(len(graph.vertices())):
		if not reached[j]:
			if has_cycle_helper(graph, j, reached, -1):
				return True
	return False
(rows, cols) = input().split()
rows = int(rows)
cols = int(cols)
grid = UndirectedGraph()
colours = {}
for row in range(rows):
	line = input()
	for col in range(cols):
		nodeIndex = row * cols + col
		colours[nodeIndex] = line[col]
		grid.add_vertex(nodeIndex)
for row in range(rows):
	for col in range(cols):
		nodeIndex = row * cols + col
		if col != 0 and colours[nodeIndex] == colours[nodeIndex - 1]:
			grid.add_edge(nodeIndex, nodeIndex - 1)
		if col != cols - 1 and colours[nodeIndex] == colours[nodeIndex + 1]:
			grid.add_edge(nodeIndex, nodeIndex + 1)
		if row != 0 and colours[nodeIndex] == colours[nodeIndex - cols]:
			grid.add_edge(nodeIndex, nodeIndex - cols)
		if row != rows - 1 and colours[nodeIndex] == colours[nodeIndex + cols]:
			grid.add_edge(nodeIndex, nodeIndex + cols)
if has_cycle(grid):
	print('Yes')
else:
	print('No')


(n, m) = map(int, input().split())
chart = []
graph = [[] for _ in ' ' * n * m]
visit = [0] * n * m
for _ in ' ' * n:
	l = []
	a = input()
	for i in a:
		l.append(i)
	chart.append(l + [' '])
chart.append([' '] * (m + 1))
for i in range(n):
	for j in range(m):
		if chart[i][j] == chart[i + 1][j]:
			graph[i * m + j].append((i + 1) * m + j)
			graph[(i + 1) * m + j].append(i * m + j)
		if chart[i][j] == chart[i][j + 1]:
			graph[i * m + j].append(i * m + j + 1)
			graph[i * m + j + 1].append(i * m + j)
for i in range(n * m):
	if visit[i]:
		continue
	visit[i] = 1
	q = [i]
	while q:
		x = q.pop()
		leaf = 1
		for y in graph[x]:
			if not visit[y]:
				leaf = 0
				visit[y] = visit[x] + 1
				q.append(y)
		if leaf:
			if len(graph[x]) > 1:
				print('Yes')
				exit(0)
print('No')


import sys
sys.setrecursionlimit(3000)
matriz = []
(n, m) = map(int, input().split())
for x in range(n):
	matriz.append(list(input()))
vis = [[False for x in range(m)] for y in range(n)]

def solve(x, y, last=-1):
	directions = [(-1, 0), (1, 0), (0, 1), (0, -1)]
	flip = [1, 0, 3, 2]
	vis[x][y] = True
	for i in range(4):
		if last == i:
			continue
		(a, b) = directions[i]
		a += x
		b += y
		if a >= 0 and b >= 0 and (a < n) and (b < m) and (matriz[a][b] == matriz[x][y]):
			if vis[a][b] or solve(a, b, flip[i]):
				return True
	return False
res = False
for x in range(n):
	for y in range(m):
		if not vis[x][y]:
			res = res or solve(x, y)
print('Yes' if res else 'No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def has_cycle(graph, vertex, visited, parent=None):
	visited.add(vertex)
	for neighbour in graph.neighbours(vertex):
		if neighbour == parent:
			continue
		if get_label(neighbour) != get_label(vertex):
			continue
		if neighbour in visited:
			return True
		if has_cycle(graph, neighbour, visited, vertex):
			return True
	return False

def get_label(vertex):
	return vertex.split('.')[0]
(rows, cols) = tuple(map(lambda e: int(e), input().split(' ')))
matrix = []
graph = UndirectedGraph()
visited = {}
for row in range(rows):
	colors = list(input())
	assert len(colors) == cols
	matrix.append([])
	for (index, color) in enumerate(colors):
		matrix[row].append('%s.%d.%d' % (color, row, index))
		visited[color] = set()
		map(lambda vertex: graph.add_vertex(vertex), '%s.%d.%d' % (color, row, index))
for row in range(rows):
	index = row * cols
	for col in range(cols):
		if col != 0:
			graph.add_edge(matrix[row][col], matrix[row][col - 1])
		if row + 1 < rows:
			graph.add_edge(matrix[row][col], matrix[row + 1][col])
		if col != cols - 1:
			graph.add_edge(matrix[row][col], matrix[row][col + 1])
		if row - 1 > 0:
			graph.add_edge(matrix[row][col], matrix[row - 1][col])
cycle = False
for vertex in graph.vertices():
	label = get_label(vertex)
	if vertex not in visited[label]:
		cycle = has_cycle(graph, vertex, visited[label], None)
		if cycle:
			break
if cycle:
	print('Yes')
else:
	print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}
	counter = 0

	def do_dfs(curr, counter):
		if curr in reached:
			return
		reached[curr] = counter
		counter += 1
		for succ in g.neighbours(curr):
			do_dfs(succ, counter)
	do_dfs(v, counter)
	return reached
userInput = input().split()
rows = int(userInput[0])
columns = int(userInput[1])
positions = []
g = UndirectedGraph()
cycle = 0
for i in range(rows):
	positions.append(list(input()))
for i in range(rows):
	for j in range(columns):
		g.add_vertex((i, j, positions[i][j]))
		if g.is_vertex((i - 1, j, positions[i][j])):
			g.add_edge((i - 1, j, positions[i][j]), (i, j, positions[i][j]))
		if g.is_vertex((i, j - 1, positions[i][j])):
			g.add_edge((i, j - 1, positions[i][j]), (i, j, positions[i][j]))
for peanut in g.vertices():
	honey = depth_first_search(g, peanut)
	for butter in honey:
		if (peanut in g.neighbours(butter)) & (honey[butter] > 2):
			cycle += 1
			break
	if cycle:
		print('Yes')
		break
if cycle == 0:
	print('No')


from queue import deque

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		if source != destination:
			self.add_vertex(source)
			self.add_vertex(destination)
			self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

def isCyclic(g):
	Q = []
	V = g.vertices()
	layer = {v: -1 for v in V}
	for v in V:
		if layer[v] != -1:
			continue
		layer[v] = 0
		Q.append(v)
		while len(Q) > 0:
			u = Q.pop(0)
			C = G.neighbours(u)
			for z in C:
				if layer[z] == -1:
					layer[z] = layer[u] + 1
					Q.append(z)
				elif layer[z] >= layer[u]:
					return True
	return False

def isCycleInGraph(g):
	todo = deque()
	layer = {vertex: -1 for vertex in g.vertices()}
	for v in g.vertices():
		if layer[v] != -1:
			continue
		layer[v] = 0
		todo.append(v)
		while todo:
			curr = todo.popleft()
			for succ in g.neighbours(curr):
				if layer[succ] == -1:
					layer[succ] = layer[curr] + 1
					todo.append(succ)
				elif layer[succ] >= layer[curr]:
					return True
	return False

def checkCycleGraph(boardArray):
	boardGraph = Graph()
	for y in range(len(boardArray)):
		for x in range(len(boardArray[y])):
			boardGraph.add_vertex((x, y))
			if x != 0 and boardArray[y][x] == boardArray[y][x - 1]:
				boardGraph.add_edge((x, y), (x - 1, y))
			if x < boardSize[1] - 1 and boardArray[y][x] == boardArray[y][x + 1]:
				boardGraph.add_edge((x, y), (x + 1, y))
			if y != 0 and boardArray[y][x] == boardArray[y - 1][x]:
				boardGraph.add_edge((x, y), (x, y - 1))
			if y < boardSize[0] - 1 and boardArray[y][x] == boardArray[y + 1][x]:
				boardGraph.add_edge((x, y), (x, y + 1))
	if isCycleInGraph(boardGraph):
		return True
	return False
boardSize = input().split()
boardSize[0] = int(boardSize[0])
boardSize[1] = int(boardSize[1])
boardArray = list()
for row in range(boardSize[0]):
	boardArray.append(list(input()))
if checkCycleGraph(boardArray):
	print('Yes')
else:
	print('No')


DIRECTIONS = [[0, 1], [0, -1], [1, 0], [-1, 0]]
(n, m) = map(int, input().split(' '))
g = []
for i in range(n):
	t = []
	for j in input():
		t.append(j)
	g.append(t)
foundLeaf = True
total = 0
while foundLeaf:
	foundLeaf = False
	for i in range(n):
		for j in range(m):
			if g[i][j].islower():
				continue
			count = 0
			for k in range(4):
				x = i + DIRECTIONS[k][0]
				y = j + DIRECTIONS[k][1]
				if x < 0 or x >= n:
					continue
				if y < 0 or y >= m:
					continue
				if g[i][j] == g[x][y]:
					count += 1
			if count <= 1:
				foundLeaf = True
				total += 1
				g[i][j] = g[i][j].lower()
print('Yes' if total < n * m else 'No')


from queue import deque
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

class WeightedGraph(Graph):

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = {}

	def add_edge(self, source, destination, weight=None):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source][destination] = weight

	def get_weight(self, source, destination):
		return self._alist[source][destination]

	def neighbours(self, vertex):
		return self._alist[vertex].keys()

	def neighbours_and_weights(self, vertex):
		return self._alist[vertex].items()

def check_adjacent(vertex, g, rows, columns, colours):
	if vertex < rows * columns - columns:
		if colours[vertex] == colours[vertex + columns]:
			g.add_edge(vertex, vertex + columns)
	if vertex % columns != columns - 1:
		if colours[vertex] == colours[vertex + 1]:
			g.add_edge(vertex, vertex + 1)

def cycle_search(g, v, counter):
	reached = {}

	def do_dfs(curr, prev, init, count):
		if curr == init and count >= 4:
			return True
		if curr in reached:
			return False
		reached[curr] = prev
		for succ in g.neighbours(curr):
			if do_dfs(succ, curr, init, count + 1):
				return True
	return do_dfs(v, v, v, counter + 1)

def construct_graph(rows, columns):
	g = UndirectedGraph()
	colours = {}
	for x in range(rows * columns):
		g.add_vertex(x)
	for x in range(rows):
		line = input()
		for y in range(columns):
			vertex = x * columns + y
			colours[vertex] = line[y]
	for x in range(rows * columns):
		check_adjacent(x, g, rows, columns, colours)
	return g
string = input()
(rows, columns) = string.split(' ')
rows = int(rows)
columns = int(columns)
g = construct_graph(rows, columns)
is_cycle = False
for v in g.vertices():
	if cycle_search(g, v, 0):
		is_cycle = True
		break
if is_cycle:
	print('Yes')
else:
	print('No')


(n, m) = [int(i) for i in input().split()]
a = []
for i in range(n):
	a.append(input())
d = {}

def dfs(x, y):
	d[x, y] = 1
	edges = []
	visited = []
	visited.append((x, y))
	q = [(x, y)]
	while q:
		(sx, sy) = q.pop(0)
		for (i, j) in [(sx + 1, sy), (sx - 1, sy), (sx, sy + 1), (sx, sy - 1)]:
			d[i, j] = 1
			if (i, j) in visited:
				if (sx, sy, i, j) not in edges:
					return (True, visited)
				else:
					continue
			if 0 <= i < n and 0 <= j < m and (a[i][j] == a[x][y]):
				visited.append((i, j))
				q.append((i, j))
				edges.append((sx, sy, i, j))
				edges.append((i, j, sx, sy))
	return False
for i in range(n):
	for j in range(m):
		if d.get((i, j)):
			continue
		if dfs(i, j):
			print('Yes')
			exit()
print('No')


def checkHeighbor(row, column, vertice, color, graph):
	(x, y) = vertice
	listVertex = []
	if x > 0 and graph[x - 1][y] == color:
		listVertex.append((x - 1, y))
	if y > 0 and graph[x][y - 1] == color:
		listVertex.append((x, y - 1))
	if x < row - 1 and graph[x + 1][y] == color:
		listVertex.append((x + 1, y))
	if y < column - 1 and graph[x][y + 1] == color:
		listVertex.append((x, y + 1))
	return listVertex

def findCircle(x, y, graph, row, column):
	color = graph[x][y]
	visited = set()
	stack = [((x, y), None)]
	while stack:
		(v, no) = stack.pop()
		if v in visited:
			return set()
		visited.add(v)
		for u in checkHeighbor(row, column, v, color, graph):
			if u != no:
				stack.append((u, v))
	return visited

def twoDots(row, column):
	graph = []
	for i in range(row):
		graph.append(input())
	visited = set()
	for i in range(row):
		for j in range(column):
			if (i, j) not in visited:
				t = findCircle(i, j, graph, row, column)
				if not t:
					print('Yes')
					exit()
				visited |= t
	print('No')
(row, column) = map(int, input().split())
twoDots(row, column)


(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')


def str_inp(n):
	return [input() for x in range(n)]

class disjointset:

	def __init__(self, n):
		(self.rank, self.parent, self.n, self.nsets, self.edges) = (defaultdict(int), defaultdict(int), n, defaultdict(int), defaultdict(int))
		for i in range(n):
			(self.parent[i], self.nsets[i]) = (i, 1)

	def find(self, x):
		if self.parent[x] == x:
			return x
		result = self.find(self.parent[x])
		self.parent[x] = result
		return result

	def union(self, x, y):
		(xpar, ypar) = (self.find(x), self.find(y))
		if xpar == ypar:
			self.edges[xpar] += 1
			return
		if self.rank[xpar] < self.rank[ypar]:
			self.parent[xpar] = ypar
			self.edges[ypar] += self.edges[xpar] + 1
			self.nsets[ypar] += self.nsets[xpar]
		elif self.rank[xpar] > self.rank[ypar]:
			self.parent[ypar] = xpar
			self.edges[xpar] += self.edges[ypar] + 1
			self.nsets[xpar] += self.nsets[ypar]
		else:
			self.parent[ypar] = xpar
			self.rank[xpar] += 1
			self.edges[xpar] += self.edges[ypar] + 1
			self.nsets[xpar] += self.nsets[ypar]

def cycle(x, y):
	return True if cnt.find(x) == cnt.find(y) else False

def dfs(i, j):
	(stack, org) = ([[i, j]], maze[i][j])
	while stack:
		(i, j) = stack.pop()
		nod1 = m * i + j
		if not visit[nod1]:
			visit[nod1] = 1
		nod2 = m * i + (j - 1)
		if j > 0 and maze[i][j - 1] == org and (not visit[nod2]):
			if cycle(nod1, nod2):
				return cnt.edges[cnt.parent[nod1]] + 1
			cnt.union(nod1, nod2)
			stack.append([i, j - 1])
		nod2 = m * i + (j + 1)
		if j < m - 1 and maze[i][j + 1] == org and (not visit[nod2]):
			if cycle(nod1, nod2):
				return cnt.edges[cnt.parent[nod1]] + 1
			cnt.union(nod1, nod2)
			stack.append([i, j + 1])
		nod2 = m * (i - 1) + j
		if i > 0 and maze[i - 1][j] == org and (not visit[nod2]):
			if cycle(nod1, nod2):
				return cnt.edges[cnt.parent[nod1]] + 1
			cnt.union(nod1, nod2)
			stack.append([i - 1, j])
		nod2 = m * (i + 1) + j
		if i < n - 1 and maze[i + 1][j] == org and (not visit[nod2]):
			if cycle(nod1, nod2):
				return cnt.edges[cnt.parent[nod1]] + 1
			cnt.union(nod1, nod2)
			stack.append([i + 1, j])
	return False
from collections import defaultdict
(n, m) = map(int, input().split())
(maze, visit, cnt) = (str_inp(n), defaultdict(int), disjointset(n * m))
for i in range(n):
	for j in range(m):
		if not visit[m * i + j]:
			num = dfs(i, j)
			if num and num >= 4:
				exit(print('Yes'))
print('No')


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

def get_node(coords):
	return graph[coords[0]][coords[1]]

def get_neighbors(coords, explored):
	(x, y) = (coords[0], coords[1])
	neighbors = []
	for c in [(x, y + 1), (x + 1, y), (x, y - 1), (x - 1, y)]:
		if 0 <= c[0] < n and 0 <= c[1] < m and (get_node(coords) == get_node(c)) and (c not in explored):
			neighbors.append(c)
	return neighbors

def two_dots():
	(explored, stack) = (set(), [])
	for i in range(n):
		for j in range(m):
			if (i, j) in explored:
				continue
			stack.append((i, j))
			while stack:
				coords = stack.pop()
				if coords in explored:
					return True
				explored.add(coords)
				node = get_node(coords)
				for neighbor in get_neighbors(coords, explored):
					stack.append(neighbor)
	return False
print('Yes' if two_dots() else 'No')


import sys
sys.setrecursionlimit(3000)
dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]

def procuraCirculo(x_atual, y_atual, de_x, de_y, cor, campo, caminho, verificados):
	if x_atual >= len(campo) or y_atual >= len(campo[0]):
		return False
	if x_atual < 0 or y_atual < 0:
		return False
	corAtual = campo[x_atual][y_atual]
	if corAtual != cor:
		return False
	if (x_atual, y_atual) in caminho:
		return True
	caminho.append((x_atual, y_atual))
	verificados[x_atual][y_atual] = True
	retorno = False
	for i in range(4):
		if retorno:
			return True
		prox_x = x_atual + dx[i]
		prox_y = y_atual + dy[i]
		if prox_x == de_x and prox_y == de_y:
			continue
		retorno = procuraCirculo(prox_x, prox_y, x_atual, y_atual, cor, campo, caminho, verificados)
	return False
entrada = input().split(' ')
linhas = int(entrada[0])
colunas = int(entrada[1])
campo = []
verificados = []
for i in range(linhas):
	entrada = input()
	campo.append(entrada)
	verificados.append([False for i in range(colunas)])
temCirculo = False
for i in range(linhas):
	for j in range(colunas):
		if temCirculo:
			break
		if verificados[i][j]:
			continue
		cor = campo[i][j]
		temCirculo = procuraCirculo(i, j, -1, -1, cor, campo, [], verificados)
	if temCirculo:
		break
if temCirculo:
	print('Yes')
else:
	print('No')


(n, m) = map(int, input().split())
S = ''
for i in range(m + 2):
	S = S + '1'
M = ['1' + input() + '1' for i in range(n)]
M.insert(0, S)
M.append(S)
t1 = [0]

def DFS(i, j):
	Visited = [[i, j]]
	while len(Visited) >= 1:
		Visited = sorted(Visited)
		v1 = Visited[0][0]
		v2 = Visited[0][1]
		del Visited[0]
		if M[v1 + 1][v2] == M[i][j] and T[v1 + 1][v2] == 0:
			Visited.append([v1 + 1, v2])
			T[v1 + 1][v2] = T[v1][v2] + 1
		if M[v1 - 1][v2] == M[i][j] and T[v1 - 1][v2] == 0:
			Visited.append([v1 - 1, v2])
			T[v1 - 1][v2] = T[v1][v2] + 1
		if M[v1][v2 + 1] == M[i][j] and T[v1][v2 + 1] == 0:
			Visited.append([v1, v2 + 1])
			T[v1][v2 + 1] = T[v1][v2] + 1
		if M[v1][v2 - 1] == M[i][j] and T[v1][v2 - 1] == 0:
			Visited.append([v1, v2 - 1])
			T[v1][v2 - 1] = T[v1][v2] + 1
	return 1
Visited = []
g = []
k = 0
T = [[0 for i in range(m + 2)] for j in range(n + 2)]
for i in range(1, n + 1):
	if t1[0] == 1:
		break
	for j in range(1, m + 1):
		if T[i][j] == 0:
			T[i][j] = 1
			DFS(i, j)
		if t1[0] == 1:
			break
for i in range(1, n + 1):
	if t1[0] == 1:
		break
	for j in range(1, m + 1):
		if T[i][j] != 1:
			k = 0
			if T[i - 1][j] == T[i][j] - 1 and M[i - 1][j] == M[i][j]:
				k = k + 1
			if T[i + 1][j] == T[i][j] - 1 and M[i + 1][j] == M[i][j]:
				k = k + 1
			if T[i][j + 1] == T[i][j] - 1 and M[i][j + 1] == M[i][j]:
				k = k + 1
			if T[i][j - 1] == T[i][j] - 1 and M[i][j - 1] == M[i][j]:
				k = k + 1
			if abs(T[i - 1][j] - T[i][j]) > 1 and M[i - 1][j] == M[i][j]:
				k = k + 1
			if abs(T[i + 1][j] - T[i][j]) > 1 and M[i + 1][j] == M[i][j]:
				k = k + 1
			if abs(T[i][j - 1] - T[i][j]) > 1 and M[i][j - 1] == M[i][j]:
				k = k + 1
			if abs(T[i][j + 1] - T[i][j]) > 1 and M[i][j + 1] == M[i][j]:
				k = k + 1
			if k >= 2:
				t1[0] = 1
				break
if t1[0] == 1:
	print('Yes')
else:
	print('No')


import sys
sys.setrecursionlimit(10000)

def validate(x, y):
	return 0 <= x < n and 0 <= y < m

def dfs(x, y, dit_x=-1, dit_y=-1):
	arr[x][y] = True
	for i in range(4):
		temp_x = x + dx[i]
		temp_y = y + dy[i]
		if validate(temp_x, temp_y) and row[x][y] == row[temp_x][temp_y]:
			if arr[temp_x][temp_y] and (not (temp_x == dit_x and temp_y == dit_y)):
				print('Yes')
				sys.exit(0)
			if not arr[temp_x][temp_y]:
				dfs(temp_x, temp_y, x, y)
(n, m) = map(int, input().split())
row = []
dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]
for i in range(n):
	row.append(input())
arr = [[False] * m for i in range(n)]
for i in range(n):
	for j in range(m):
		if not arr[i][j]:
			dfs(i, j)
print('No')


(n, m) = map(int, input().split())
arr = [input() for _ in range(n)]
stack = [(0, 0)]
visited = set()
start = (0, 0)
succ = False
last = [[(-1, -1) for _ in range(m)] for _ in range(n)]
path = []

def is_neig(a, b):
	return abs(a[0] - b[0]) + abs(a[1] - b[1]) == 1
while len(visited) != n * m:
	if not stack:
		find = False
		for i in range(n):
			for j in range(m):
				if (i, j) not in visited:
					stack.append((i, j))
					start = (i, j)
					path = []
					find = True
					break
			if find:
				break
	p = stack.pop()
	if not path or is_neig(path[-1], p):
		path.append(p)
	else:
		path = path[:path.index(last[p[0]][p[1]]) + 1] + [p]
	visited.add(p)
	for i in [-1, 1]:
		if len(path) >= 4:
			for x in path[:-3]:
				if x == (p[0] + i, p[1]) or x == (p[0], p[1] + i):
					succ = True
					break
		if succ:
			break
		if 0 <= p[0] + i < n and (p[0] + i, p[1]) not in visited and (arr[p[0]][p[1]] == arr[p[0] + i][p[1]]):
			if last[p[0] + i][p[1]] == (-1, -1):
				last[p[0] + i][p[1]] = p
			stack.append((p[0] + i, p[1]))
		if 0 <= p[1] + i < m and (p[0], p[1] + i) not in visited and (arr[p[0]][p[1]] == arr[p[0]][p[1] + i]):
			if last[p[0]][p[1] + i] == (-1, -1):
				last[p[0]][p[1] + i] = p
			stack.append((p[0], p[1] + i))
	if succ:
		break
if succ:
	print('Yes')
else:
	print('No')


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')


def dfs(x, y, x1, y1):
	v.add((x, y))
	for (dx, dy) in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
		if x + dx >= 0 and x + dx < n and (y + dy >= 0) and (y + dy < m) and ((x + dx, y + dy) != (x1, y1)) and (a[x + dx][y + dy] == a[x][y]):
			if (x + dx, y + dy) in v or dfs(x + dx, y + dy, x, y):
				return True
	return False
import sys
sys.setrecursionlimit(10000)
(n, m) = map(int, input().split())
a = [input() for i in range(n)]
v = set()
for i in range(n):
	for j in range(m):
		if not (i, j) in v and dfs(i, j, -1, -1):
			print('Yes')
			quit()
print('No')


from collections import defaultdict
import sys

class Graph:

	def __init__(self):
		self.graph = defaultdict(list)

	def addEdge(self, u, v):
		self.graph[u].append(v)

	def vertices(self):
		return self.graph.keys()

	def neighbor(self, u):
		return self.graph[u]

	def DFS(self, v):
		visited = {}
		a = 0

		def DFSUtil(curr, prev):
			if curr in visited:
				print('Yes')
				quit()
			visited[curr] = prev
			for i in self.neighbor(curr):
				if i != prev:
					DFSUtil(i, curr)
		DFSUtil(v, v)
		return visited

def find_poss():
	a = input().split()
	m = int(a[0])
	n = int(a[1])
	a = Graph()
	s = [list(input()) for i in range(m)]
	for i in range(n):
		for j in range(m):
			if i + 1 < n and s[j][i] == s[j][i + 1]:
				a.addEdge(i + j * n, i + j * n + 1)
				a.addEdge(i + j * n + 1, i + j * n)
			if j + 1 < m and s[j][i] == s[j + 1][i]:
				a.addEdge(i + j * n, i + (j + 1) * n)
				a.addEdge(i + (j + 1) * n, i + j * n)
	return a
sys.setrecursionlimit(1500)
a = find_poss()
b = a.vertices()
visited = set()
for i in b:
	if i not in visited:
		visited.update(a.DFS(i))
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()


import sys
sys.setrecursionlimit(10 ** 6)

def dfs(x, y, prex=0, prey=0):
	visited[x][y] = True
	for (dx, dy) in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
		(nxtx, nxty) = (x + dx, y + dy)
		if not (0 <= nxtx < n and 0 <= nxty < m and (dmatrix[nxtx][nxty] == dmatrix[x][y])):
			continue
		if visited[nxtx][nxty]:
			if nxtx != prex or nxty != prey:
				return True
		elif dfs(nxtx, nxty, x, y):
			return True
	return False
(n, m) = [int(i) for i in input().split()]
visited = [[False] * m for _ in range(n)]
dmatrix = []
for i in range(n):
	t = input()
	dmatrix.append(t)

class Found(Exception):
	pass
try:
	for i in range(n):
		for j in range(m):
			if not visited[i][j]:
				if dfs(i, j):
					raise Found()
except Found:
	print('Yes')
else:
	print('No')


read = lambda : map(int, input().split())
(n, m) = read()
a = [input() for i in range(n)]
g = [list() for i in range(n * m)]
f = lambda x, y: x * m + y
for i in range(n):
	for j in range(m):
		c = a[i][j]
		ind = f(i, j)
		if i > 0 and c == a[i - 1][j]:
			g[ind].append(f(i - 1, j))
		if i < n - 1 and c == a[i + 1][j]:
			g[ind].append(f(i + 1, j))
		if j > 0 and c == a[i][j - 1]:
			g[ind].append(f(i, j - 1))
		if j < m - 1 and c == a[i][j + 1]:
			g[ind].append(f(i, j + 1))

def dfs(v, p=-1):
	was[v] = True
	for u in g[v]:
		if not was[u]:
			dfs(u, v)
		elif u != p:
			print('Yes')
			exit()
was = [False] * (n * m)
for i in range(n * m):
	if not was[i]:
		dfs(i)
print('No')


from collections import defaultdict
(n, m) = map(int, input().split())
grid = [input() for _ in range(n)]
graph = defaultdict(set)
for x in range(n - 1):
	for y in range(m):
		if grid[x][y] == grid[x + 1][y]:
			id1 = x * m + y
			id2 = (x + 1) * m + y
			graph[id1].add(id2)
			graph[id2].add(id1)
for x in range(n):
	for y in range(m - 1):
		if grid[x][y] == grid[x][y + 1]:
			id1 = x * m + y
			id2 = x * m + y + 1
			graph[id1].add(id2)
			graph[id2].add(id1)
seenKeys = set()
for (k, v) in graph.items():
	if k in seenKeys:
		continue
	seenKeys.add(k)
	seen = set()
	queue = [k]
	start = k
	parent = defaultdict(int)
	while queue:
		current = queue.pop(0)
		if current in seen:
			print('Yes')
			exit()
		for n in graph[current]:
			if n != parent[current]:
				parent[n] = current
				queue.append(n)
		seen.add(current)
	seenKeys.update(seen)
print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def get_input_info(g):
	for row in range(n):
		line = input()
		for col in range(len(line)):
			g.add_vertex((col, row, line[col]))
			if row != 0 and g.is_vertex((col, row - 1, line[col])):
				g.add_edge((col, row, line[col]), (col, row - 1, line[col]))
			if col != 0 and g.is_vertex((col - 1, row, line[col])):
				g.add_edge((col, row, line[col]), (col - 1, row, line[col]))

def depth_first_search(g, v):
	reached = {}
	stepCounter = 0

	def do_dfs(curr, stepCounter):
		if curr in reached:
			return
		reached[curr] = stepCounter
		stepCounter += 1
		for succ in g.neighbours(curr):
			do_dfs(succ, stepCounter)
	do_dfs(v, stepCounter)
	return reached

def find_cycle(g):
	for v in g.vertices():
		reached = depth_first_search(g, v)
		for vertex2 in reached:
			if v in g.neighbours(vertex2) and reached[vertex2] > 2:
				return True
	return False
(n, m) = input().split()
n = int(n)
m = int(m)
my_graph = UndirectedGraph()
layout = get_input_info(my_graph)
if find_cycle(my_graph):
	print('Yes')
else:
	print('No')


import sys
from math import log2, floor, ceil, sqrt
sys.setrecursionlimit(10 ** 5)
Ri = lambda : [int(x) for x in sys.stdin.readline().split()]
ri = lambda : sys.stdin.readline().strip()

def input():
	return sys.stdin.readline().strip()

def list2d(a, b, c):
	return [[c] * b for i in range(a)]

def list3d(a, b, c, d):
	return [[[d] * c for j in range(b)] for i in range(a)]

def list4d(a, b, c, d, e):
	return [[[[e] * d for j in range(c)] for j in range(b)] for i in range(a)]

def ceil(x, y=1):
	return int(-(-x // y))

def INT():
	return int(input())

def MAP():
	return map(int, input().split())

def LIST(N=None):
	return list(MAP()) if N is None else [INT() for i in range(N)]

def Yes():
	print('Yes')

def No():
	print('No')

def YES():
	print('YES')

def NO():
	print('NO')
INF = 10 ** 18
MOD = 998244353

def dfs(x, y, px, py):
	global flag
	if flag:
		return
	if x < 0 or x >= n or y < 0 or (y >= m):
		return
	if px == -1 and py == -1 or lis[x][y] == lis[px][py]:
		if vis[x][y]:
			flag = True
			return
		vis[x][y] = True
		if x + 1 != px:
			dfs(x + 1, y, x, y)
		if x - 1 != px:
			dfs(x - 1, y, x, y)
		if y + 1 != py:
			dfs(x, y + 1, x, y)
		if y - 1 != py:
			dfs(x, y - 1, x, y)
(n, m) = Ri()
lis = []
flag = False
for i in range(n):
	temp = ri()
	lis.append(temp)
vis = list2d(n, m, False)
for i in range(n):
	for j in range(m):
		if not vis[i][j]:
			dfs(i, j, -1, -1)
if flag:
	Yes()
else:
	No()


import sys
input = sys.stdin.readline
sys.setrecursionlimit(2 ** 16)

def peers(x):
	(peers, colour) = ([], plan[x[0]][x[1]])
	if 0 <= x[0] + 1 < n and plan[x[0] + 1][x[1]] == colour:
		peers.append((x[0] + 1, x[1]))
	if 0 <= x[0] - 1 < n and plan[x[0] - 1][x[1]] == colour:
		peers.append((x[0] - 1, x[1]))
	if 0 <= x[1] + 1 < m and plan[x[0]][x[1] + 1] == colour:
		peers.append((x[0], x[1] + 1))
	if 0 <= x[1] - 1 < m and plan[x[0]][x[1] - 1] == colour:
		peers.append((x[0], x[1] - 1))
	return peers

def dfs(start, last):
	if start in searched:
		global flag
		flag = True
		return
	searched.add(start)
	for peer in peers(start):
		if peer != last:
			dfs(peer, start)
(n, m) = map(int, input().split())
(plan, searched, flag) = ([[char for char in input().strip()] for _ in range(n)], set(), False)
for i in range(n):
	for j in range(m):
		if (i, j) not in searched:
			dfs((i, j), (-1, -1))
if flag:
	print('Yes')
else:
	print('No')


import sys
x = 10 ** 5
sys.setrecursionlimit(x)
(rows, columns) = map(int, input().split())
matrix = []
visited = []
cycle = False
for i in range(rows):
	row = [char for char in input()]
	visited_row = []
	for i in row:
		visited_row.append(False)
	visited.append(visited_row)
	matrix.append(row)
x = [1, -1, 0, 0]
y = [0, 0, 1, -1]

def check_cycle(node, came_from, letter):
	global matrix, visited
	if node[0] < 0 or node[1] < 0 or node[0] >= rows or (node[1] >= columns):
		return
	if matrix[node[0]][node[1]] != letter:
		return
	if visited[node[0]][node[1]]:
		global cycle
		cycle = True
		return
	visited[node[0]][node[1]] = True
	for i in range(4):
		new_r = node[0] + x[i]
		new_c = node[1] + y[i]
		if (new_r, new_c) == came_from:
			continue
		check_cycle((new_r, new_c), node, letter)
for r in range(rows):
	for c in range(columns):
		if not visited[r][c]:
			check_cycle((r, c), (-1, -1), matrix[r][c])
if cycle:
	print('Yes')
else:
	print('No')


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

class WeightedGraph(Graph):

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = {}

	def add_edge(self, source, destination, weight=None):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source][destination] = weight

	def get_weight(self, source, destination):
		return self._alist[source][destination]

	def neighbours(self, vertex):
		return self._alist[vertex].keys()

	def neighbours_and_weights(self, vertex):
		return self._alist[vertex].items()
from queue import deque

def breadth_first_search(g, v):
	reached = {v: v}
	todo = deque([v])
	while todo:
		curr = todo.popleft()
		for succ in g.neighbours(curr):
			if succ not in reached:
				reached[succ] = curr
				todo.append(succ)
	return reached

def get_components(g):
	comps = []
	d = set()
	for v in g.vertices():
		if not v in d:
			temp = breadth_first_search(g, v)
			nodes = set()
			for p in temp:
				d.add(p)
				nodes.add(p)
			comps.append(nodes)
	return comps

def cycle(g, v):
	reached = {v: v}
	todo = deque([v])
	while todo:
		curr = todo.pop()
		for succ in g.neighbours(curr):
			if succ not in reached:
				reached[succ] = curr
				todo.append(succ)
			elif reached[curr] != succ:
				return True
	return False

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

def read_graph():
	g = UndirectedGraph()
	dim = input().split(' ')
	n = int(dim[0])
	m = int(dim[1])
	lines = []
	for i in range(n):
		lines.append(input().strip())
	for i in range(n):
		for j in range(m):
			g.add_vertex((i, j))
			if valid(n, m, i - 1, j):
				if lines[i][j] == lines[i - 1][j]:
					g.add_edge((i, j), (i - 1, j))
			if valid(n, m, i, j - 1):
				if lines[i][j] == lines[i][j - 1]:
					g.add_edge((i, j), (i, j - 1))
	return g

def cycles(g):
	for c in get_components(g):
		if cycle(g, c.pop()):
			return True
	return False
g = read_graph()
if cycles(g):
	print('Yes')
else:
	print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
size_arr = input().split(' ')
max_i = int(size_arr[0]) - 1
max_j = int(size_arr[1]) - 1

def adj_check(v, d):
	adj = []
	if v[1] > 0 and d[v] == d[v[0], v[1] - 1]:
		adj.append((v[0], v[1] - 1))
	if v[1] < max_j and d[v] == d[v[0], v[1] + 1]:
		adj.append((v[0], v[1] + 1))
	if v[0] > 0 and d[v] == d[v[0] - 1, v[1]]:
		adj.append((v[0] - 1, v[1]))
	if v[0] < max_i and d[v] == d[v[0] + 1, v[1]]:
		adj.append((v[0] + 1, v[1]))
	return adj

def depth_first_search(v, d):
	reached = {}
	order = []

	def do_dfs(curr, prev):
		if curr in reached:
			order.append(curr)
			return
		reached[curr] = prev
		neighbours = adj_check(curr, d)
		if prev in neighbours:
			neighbours.remove(prev)
		for succ in neighbours:
			do_dfs(succ, curr)
		order.append(curr)
	do_dfs(v, v)
	if len(order) != len(set(order)):
		return False
	return reached
d = {}
reached = {}
result = 'No'
for i in range(int(size_arr[0])):
	line = input()
	for j in range(int(size_arr[1])):
		d[i, j] = line[j]
for i in range(int(size_arr[0])):
	for j in range(int(size_arr[1])):
		if (i, j) not in reached:
			buff = depth_first_search((i, j), d)
			if buff == False:
				result = 'Yes'
				break
			else:
				reached.update(buff)
	if result == 'Yes':
		break
print(result)


import sys
sys.setrecursionlimit(3000)
(n, m) = map(int, input().split())
g = []
used = []
for _ in range(n):
	g.append(input())
	used.append([False] * m)
answer = 'No'

def dfs(i, j, previ, prevj, letter):
	global used
	global answer
	used[i][j] = True
	for move in [(i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)]:
		if 0 <= move[0] < n and 0 <= move[1] < m and (not used[move[0]][move[1]]) and (letter == g[move[0]][move[1]]):
			dfs(move[0], move[1], i, j, letter)
		elif 0 <= move[0] < n and 0 <= move[1] < m and used[move[0]][move[1]] and (letter == g[move[0]][move[1]]) and (move[0] != previ or move[1] != prevj):
			answer = 'Yes'
for i in range(n):
	for j in range(m):
		if not used[i][j]:
			dfs(i, j, -1, -1, g[i][j])
print(answer)


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
(x, y) = input().split()
x = int(x)
y = int(y)
universe = []
visited = []
for i in range(x):
	buff = list(input())
	universe.append(buff)

def loop(h, k, previousx, previousy):
	stuff = [[0, 1], [-1, 0], [0, -1], [1, 0]]
	for stuff1 in stuff:
		if h + stuff1[0] >= 0 and h + stuff1[0] < x and (k + stuff1[1] >= 0) and (k + stuff1[1] < y) and (universe[h][k] == universe[h + stuff1[0]][k + stuff1[1]]):
			if h + stuff1[0] == previousx and k + stuff1[1] == previousy:
				continue
			if [h + stuff1[0], k + stuff1[1]] in visited:
				print('Yes')
				exit()
			visited.append([h + stuff1[0], k + stuff1[1]])
			loop(h + stuff1[0], k + stuff1[1], h, k)
for n in range(x):
	for m in range(y):
		if [n, m] in visited:
			continue
		loop(n, m, -1, -1)
		visited.append([n, m])
print('No')


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
r_c = input().split(' ')
rows = int(r_c[0])
columns = int(r_c[1])
g = UndirectedGraph()
vertices = dict()
cycle = 'No'
v = 0
for r in range(rows):
	row = input()
	for c in range(columns):
		g.add_vertex(v)
		vertices[v] = row[c]
		v += 1
v = 0
for r in range(rows):
	for c in range(columns - 1):
		if vertices[v] is vertices[v + 1]:
			g.add_edge(v, v + 1)
		v += 1
	v += 1
v = 0
for r in range(rows - 1):
	for c in range(columns):
		if vertices[v] is vertices[v + columns]:
			g.add_edge(v, v + columns)
		v += 1
for v in range(rows * columns):
	reached = depth_first_search(g, v)
	for n in g.neighbours(v):
		if reached[n] is not v:
			cycle = 'Yes'
			break
	if cycle is 'Yes':
		break
print(cycle)


dx = [-1, 1, 0, 0]
dy = [0, 0, 1, -1]

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

def DFS(i, j, parX, parY):
	v[i][j] = True
	stack = [[i, j, -1, -1]]
	while stack:
		a = stack.pop()
		(A, B, parX, parY) = a
		for k in range(4):
			(newA, newB) = (A + dx[k], B + dy[k])
			if valid(newA, newB, parX, parY):
				if l[newA][newB] == l[A][B]:
					if v[newA][newB]:
						return True
					stack.append([newA, newB, A, B])
					v[newA][newB] = True
	return False
(n, m) = map(int, input().split())
l = []
for i in range(n):
	s = list(input())
	l.append(s)
c = 0
v = [[False for i in range(m)] for j in range(n)]
for i in range(n):
	for j in range(m):
		if not v[i][j]:
			ans = DFS(i, j, -1, -1)
			if ans:
				print('Yes')
				c = 1
				break
	if c:
		break
if c == 0:
	print('No')


import sys
sys.setrecursionlimit(10 ** 5)
cor = []
adj = []
grid = []
(n, m) = input().split()
n = int(n)
m = int(m)
for i in range(n):
	entr = input()
	cor.append([])
	adj.append([])
	tmp = []
	for j in range(m):
		cor[i].append(0)
		adj[i].append([])
		tmp.append(entr[j])
		if j and entr[j] == entr[j - 1]:
			adj[i][j].append((i, j - 1))
			adj[i][j - 1].append((i, j))
		if i and grid[i - 1][j] == entr[j]:
			adj[i][j].append((i - 1, j))
			adj[i - 1][j].append((i, j))
	grid.append(tmp)

def encontraCiclo(i, j, ant):
	cor[i][j] = 1
	for atual in adj[i][j]:
		if atual == ant:
			continue
		if cor[atual[0]][atual[1]] == 0:
			encontraCiclo(atual[0], atual[1], (i, j))
		if cor[atual[0]][atual[1]] in [1, 3]:
			cor[i][j] = 3
	if cor[i][j] == 1:
		cor[i][j] = 2
res = 'No'
for i in range(n):
	for j in range(m):
		if cor[i][j] == 0:
			encontraCiclo(i, j, (-1, -1))
			if cor[i][j] == 3:
				res = 'Yes'
print(res)


from collections import Counter, defaultdict, deque
import heapq as hq
import math
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 6)

def dfs(cur, par):
	global visited, matrix, n, m
	curchar = matrix[cur[0]][cur[1]]
	visited[cur[0]][cur[1]] = True
	dirs = [[1, 0], [0, 1], [-1, 0], [0, -1]]
	for el in dirs:
		newi = el[0] + cur[0]
		newj = el[1] + cur[1]
		if (newi, newj) == par:
			continue
		if 0 <= newi < n and 0 <= newj < m and (matrix[newi][newj] == curchar):
			if visited[newi][newj]:
				return True
			elif dfs((newi, newj), cur):
				return True
	return False
(n, m) = [int(x) for x in input().split()]
matrix = [input().strip() for x in range(n)]
visited = [[False] * m for x in range(n)]
for i in range(n):
	for j in range(m):
		if not visited[i][j]:
			if dfs((i, j), -1):
				print('Yes')
				exit()
print('No')


import math
import sys
(n, m) = map(int, input().split())
field = [list('0' * (m + 2))]
count = [[0] * (m + 2)]
for i in range(n):
	s = ['0'] + list(str(input())) + ['0']
	field.append(s)
	count.append([0] * (m + 2))
field.append(list('0' * (m + 2)))
count.append([0] * (m + 2))
flag = False
vector = [[-1, 0], [1, 0], [0, -1], [0, 1]]
for i in range(1, n + 1):
	for j in range(1, m + 1):
		if count[i][j] == 0:
			count[i][j] = 1
			q = [[i, j]]
			color = field[i][j]
			while q != []:
				r = 0
				x = q[0][1]
				y = q[0][0]
				for direct in range(4):
					new_x = x + vector[direct][0]
					new_y = y + vector[direct][1]
					if field[new_y][new_x] == color:
						if count[new_y][new_x] == 0:
							count[new_y][new_x] = 1
							q.append([new_y, new_x])
						else:
							r += 1
				if r >= 2:
					flag = True
				q.pop(0)
if flag:
	print('Yes')
else:
	print('No')


import sys
MOD = 10 ** 9 + 7
INF = float('inf')
input = lambda : sys.stdin.readline().strip()
sys.setrecursionlimit(10 ** 8)

def solve():

	def judge(y, x, depth):
		if y == i and x == j:
			if depth >= 4:
				print('Yes')
				sys.exit()
		if check[y][x]:
			check[y][x] = 0
			dfs(y, x, depth + 1)
			check[y][x] = 1

	def dfs(y, x, depth):
		if info[y][x] == info[y][x - 1]:
			judge(y, x - 1, depth)
		if info[y][x] == info[y - 1][x]:
			judge(y - 1, x, depth)
		if info[y][x] == info[y + 1][x]:
			judge(y + 1, x, depth)
		if info[y][x] == info[y][x + 1]:
			judge(y, x + 1, depth)
	(n, m) = map(int, input().split())
	info = [['#'] * (m + 2)]
	for _ in range(n):
		info.append(['#'] + list(input()) + ['#'])
	info.append(['#'] * (m + 2))
	check = [[1] * (m + 2) for _ in range(n + 2)]
	for i in range(1, n, 2):
		for j in range(1, m, 2):
			check[i][j] = 0
			dfs(i, j, 1)
			check[i][j] = 1
	print('No')
solve()


(n, m) = map(int, input().split())
matrix = [input() for x in range(n)]
visited = [[False for x1 in range(m)] for x2 in range(n)]
count = 0
queue = []
root = 0
res = False
while count < n * m or len(queue) > 0:
	if len(queue) == 0:
		while visited[root // m][root % m]:
			root += 1
		queue.append((-1, root))
	(prev, curr) = queue.pop()
	if visited[curr // m][curr % m]:
		res = True
		break
	visited[curr // m][curr % m] = True
	count += 1
	if curr >= m and prev != curr - m and (matrix[curr // m][curr % m] == matrix[curr // m - 1][curr % m]):
		queue.insert(0, (curr, curr - m))
	if curr < (n - 1) * m and prev != curr + m and (matrix[curr // m][curr % m] == matrix[curr // m + 1][curr % m]):
		queue.insert(0, (curr, curr + m))
	if curr % m < m - 1 and prev != curr + 1 and (matrix[curr // m][curr % m] == matrix[curr // m][curr % m + 1]):
		queue.insert(0, (curr, curr + 1))
	if curr % m > 0 and prev != curr - 1 and (matrix[curr // m][curr % m] == matrix[curr // m][curr % m - 1]):
		queue.insert(0, (curr, curr - 1))
if res:
	print('Yes')
else:
	print('No')


(n, m) = map(int, input().split())
g = [None for i in range(n)]
for j in range(n):
	g[j] = input()
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]
vis = [[0 for j in range(m)] for i in range(n)]

def valid(x, y):
	global n, m
	return x >= 0 and x < n and (y >= 0) and (y < m)

def cycle(x, y, k, px, py):
	global dx, dy, vis, color
	if vis[x][y] == 1:
		if k >= 4:
			return True
		return False
	vis[x][y] = 1
	for i in range(4):
		if valid(x + dx[i], y + dy[i]) and (x + dx[i], y + dy[i]) != (px, py) and (g[x][y] == g[x + dx[i]][y + dy[i]]):
			if cycle(x + dx[i], y + dy[i], k + 1, x, y):
				return True
	return False

def solve():
	for i in range(n):
		for j in range(m):
			if cycle(i, j, 1, -1, -1):
				print('Yes')
				return 0
	print('No')
solve()


import sys
sys.setrecursionlimit(4000)

def neig(d, prev):
	(x, y) = d
	out = list()
	if x + 1 < n:
		out.append((x + 1, y))
	if x - 1 >= 0:
		out.append((x - 1, y))
	if y + 1 < m:
		out.append((x, y + 1))
	if y - 1 >= 0:
		out.append((x, y - 1))
	if prev != tuple():
		out.remove(prev)
	return out

def DFS(v, prev=tuple()):
	(a, b) = v
	used[a][b] = True
	for u in neig(v, prev):
		(g, h) = u
		if not used[g][h] and field[g][h] == P:
			DFS(u, prev=v)
		elif field[g][h] == P:
			print('Yes')
			exit()
(n, m) = map(int, input().split())
field = [input() for i in range(n)]
used = [[False for j in range(m)] for k in range(n)]
for i in range(n):
	for j in range(m):
		if not used[i][j]:
			P = field[i][j]
			DFS((i, j))
print('No')


import itertools
import bisect
import math
from collections import *
import os
import sys
from io import BytesIO, IOBase
ii = lambda : int(input())
lmii = lambda : list(map(int, input().split()))
slmii = lambda : sorted(map(int, input().split()))
li = lambda : list(input())
mii = lambda : map(int, input().split())
msi = lambda : map(str, input().split())
sys.setrecursionlimit(2500)

def dfs(i, j, vis, adj):
	vis[i, j] = 1
	cnt = 0
	for ele in adj[i, j]:
		if vis[ele[0], ele[1]] == 0:
			dfs(ele[0], ele[1], vis, adj)
		else:
			cnt += 1
	if cnt > 1:
		print('Yes')
		exit()

def main():
	(n, m) = mii()
	lst = []
	for i in range(n):
		lst.append(li())
	adj = defaultdict(list)
	for i in range(n):
		for j in range(m):
			if i != 0:
				if lst[i - 1][j] == lst[i][j]:
					adj[i, j].append([i - 1, j])
			if i != n - 1:
				if lst[i + 1][j] == lst[i][j]:
					adj[i, j].append([i + 1, j])
			if j != 0:
				if lst[i][j - 1] == lst[i][j]:
					adj[i, j].append([i, j - 1])
			if j != m - 1:
				if lst[i][j + 1] == lst[i][j]:
					adj[i, j].append([i, j + 1])
	for i in range(n):
		for j in range(m):
			vis = defaultdict(int)
			dfs(i, j, vis, adj)
	print('No')
	pass
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


import sys

def findColor(visited, m, n):
	for i in range(m):
		for j in range(n):
			if visited[i][j] == False:
				return (i, j)
	return -1

def dfs(array, visited, directions, i, j, iConstant, jConstant, result=False, aux=0):
	parent = (i, j)
	visited[i][j] = True
	for (di, dj) in directions:
		if 0 <= i + di < m and 0 <= j + dj < n:
			if i + di == iConstant and j + dj == jConstant and (i != aux[0]) and (j != aux[1]):
				if parent[0] != iConstant or parent[1] != jConstant:
					return True
			if visited[i + di][j + dj] == False:
				if array[i + di][j + dj] == array[i][j]:
					if aux == 0:
						aux = (i + di, j + dj)
					parent = (i + di, j + dj)
					result = dfs(array, visited, directions, i + di, j + dj, iConstant, jConstant, result, aux)
		if result:
			return result
		if i == iConstant and j == jConstant:
			aux = 0
	return False

def solve(visited, array, directions, m, n, result=False):
	index = findColor(visited, m, n)
	counter = 0
	while index is not -1:
		for (x, y) in directions:
			if 0 <= index[0] + x < m and 0 <= index[1] + y < n:
				if array[index[0] + x][index[1] + y] == array[index[0]][index[1]]:
					counter += 1
					if counter >= 2:
						break
		if counter < 2:
			visited[index[0]][index[1]] = True
		if counter >= 2:
			test = edgeCase(array, index[0], index[1], m, n)
			if test:
				return 'Yes'
			result = dfs(array, visited, directions, index[0], index[1], index[0], index[1])
			if result:
				return 'Yes'
		counter = 0
		index = findColor(visited, m, n)
	return 'No'

def edgeCase(array, i, j, m, n):
	if 0 <= i + 1 < m and 0 <= j + 1 < n:
		if array[i + 1][j + 1] == array[i][j] and array[i + 1][j] == array[i][j] and (array[i][j + 1] == array[i][j]):
			return True
	if 0 <= i + 1 < m and 0 <= j - 1 < n:
		if array[i + 1][j - 1] == array[i][j] and (array[i + 1][j] == array[i][j] and array[i][j - 1] == array[i][j]):
			return True
	if 0 <= i - 1 < m and 0 <= j + 1 < n:
		if array[i - 1][j + 1] == array[i][j] and (array[i - 1][j] == array[i][j] and array[i][j + 1] == array[i][j]):
			return True
	if 0 <= i - 1 < n and 0 <= j - 1 < n:
		if array[i - 1][j - 1] == array[i][j] and (array[i - 1][j] == array[i][j] and array[i][j - 1] == array[i][j]):
			return True
	return False
x = 3000
sys.setrecursionlimit(x)
(m, n) = map(int, input().split())
array = [list(input()) for x in range(m)]
visited = [[False] * n for x in range(m)]
directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
result = solve(visited, array, directions, m, n)
print(result)


(n, m) = map(int, input().split(' '))
s = [list(input() + '.') for i in range(n)] + [list('.' * (m + 1))]

def trans():
	flag = False
	for i in range(n):
		for j in range(m):
			if s[i][j] != '.':
				ch = s[i][j]
				d = 0
				d += s[i + 1][j] == ch
				d += s[i - 1][j] == ch
				d += s[i][j + 1] == ch
				d += s[i][j - 1] == ch
				if d < 2:
					s[i][j] = '.'
					flag = True
	return flag
while trans():
	flag = True
for i in range(n):
	for j in range(m):
		if s[i][j] != '.':
			print('Yes')
			quit()
print('No')


(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')


(n, m) = map(int, input().split())
board = [input() for _ in range(n)]
visited = [[0] * m for _ in range(n)]
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]

def previous(cur, pre):
	return cur[0] == -pre[0] and cur[1] == -pre[1]

def search(target, pre, row, line):
	print(row, line)
	if not 0 <= row < n or not 0 <= line < m or board[row][line] != target:
		return
	if visited[row][line]:
		return 1
	visited[row][line] = 1
	for d in directions:
		if not previous(d, pre) and search(target, d, row + d[0], line + d[1]):
			return 1

def start(row, line):
	if visited[row][line]:
		return
	visited[row][line] = 1
	target = board[row][line]
	stack = []
	for i in directions:
		stack.append((i, row + i[0], line + i[1]))
	while stack:
		(pre, r, l) = stack.pop()
		if not 0 <= r < n or not 0 <= l < m or board[r][l] != target:
			continue
		if visited[r][l]:
			return 1
		visited[r][l] = 1
		for d in directions:
			if not previous(pre, d):
				stack.append((d, r + d[0], l + d[1]))

def main():
	for r in range(n):
		for li in range(m):
			if start(r, li):
				print('Yes')
				return
	print('No')
	return
main()


flag = False

def dfs(i, j, n, m, mat, visited, parent):
	global flag
	if i < 0 or j < 0 or i >= n or (j >= m):
		return
	visited[i][j] = 1
	if i - 1 >= 0 and mat[i - 1][j] == mat[i][j]:
		if parent[0] != i - 1 or parent[1] != j:
			if visited[i - 1][j] == 1:
				flag = True
				return
			else:
				dfs(i - 1, j, n, m, mat, visited, [i, j])
	if j - 1 >= 0 and mat[i][j - 1] == mat[i][j]:
		if parent[0] != i or parent[1] != j - 1:
			if visited[i][j - 1] == 1:
				flag = True
				return
			else:
				dfs(i, j - 1, n, m, mat, visited, [i, j])
	if i + 1 < n and mat[i + 1][j] == mat[i][j]:
		if parent[0] != i + 1 or parent[1] != j:
			if visited[i + 1][j] == 1:
				flag = True
				return
			else:
				dfs(i + 1, j, n, m, mat, visited, [i, j])
	if j + 1 < m and mat[i][j + 1] == mat[i][j]:
		if parent[0] != i or parent[1] != j + 1:
			if visited[i][j + 1] == 1:
				flag = True
				return
			else:
				dfs(i, j + 1, n, m, mat, visited, [i, j])
(n, m) = map(int, input().split())
mat = []
visited = [[0 for i in range(m)] for i in range(n)]
for i in range(n):
	mat.append(list(input()))
for i in range(n):
	for j in range(m):
		if visited[i][j] == 0:
			dfs(i, j, n, m, mat, visited, [-1, -1])
			if flag == True:
				break
	if flag == True:
		break
print('Yes' if flag == True else 'No')


from sys import setrecursionlimit
setrecursionlimit(10000)

def circle_found(chart, i, j, previous_i, previous_j):
	path[i][j] = True
	di = [-1, 1, 0, 0]
	dj = [0, 0, -1, 1]
	for t in range(4):
		I = i + di[t]
		J = j + dj[t]
		if (0 <= I < n and 0 <= J < m) and (I != previous_i or J != previous_j) and (chart[i][j] == chart[I][J]):
			if path[I][J]:
				return True
			elif circle_found(chart, I, J, i, j):
				return True
	return False

def find_circle(chart, n, m):
	for i in range(n - 1):
		for j in range(m):
			if not path[i][j]:
				if circle_found(chart, i, j, 0, 0):
					return True
	return False
(n, m) = [int(i) for i in input().split()]
path = [[False] * m for _ in range(n)]
chart = []
for _ in range(n):
	chart.append(list(input()))
if find_circle(chart, n, m):
	print('Yes')
else:
	print('No')


def dfs(x, y, c, pa, pb):
	v[x][y] = c
	d = [[-1, 0], [1, 0], [0, -1], [0, 1]]
	for (i, j) in d:
		a = x + i
		b = y + j
		if a >= 0 and a < n and (0 <= b < m):
			if not v[a][b] and g[a][b] == c:
				dfs(a, b, c, x, y)
			elif v[a][b] == c and a != pa and (b != pb):
				print('Yes')
				exit()
(n, m) = map(int, input().split())
g = [list(input()) for i in range(n)]
v = [[0] * m for i in range(n)]
for i in range(n):
	for j in range(m):
		if not v[i][j]:
			dfs(i, j, g[i][j], -1, -1)
print('No')


from sys import *
setrecursionlimit(5000)
(n, m) = map(int, input().split())
g = [input() for i in range(n)]
vis = [[False] * m for i in range(n)]
d = [(1, 0), (-1, 0), (0, -1), (0, 1)]

def ok(y, x):
	return 0 <= x and 0 <= y and (x < m) and (y < n)

def dfs(v, par, vis, ans):
	vis[v[0]][v[1]] = True
	for (ii, jj) in d:
		ni = v[0] + ii
		nj = v[1] + jj
		if ok(ni, nj) and vis[ni][nj] and (g[ni][nj] == g[v[0]][v[1]]) and ((ni, nj) != par):
			ans.append(1)
		elif ok(ni, nj) and (not vis[ni][nj]) and (g[ni][nj] == g[v[0]][v[1]]):
			dfs((ni, nj), v, vis, ans)
ans = []
for i in range(n):
	for j in range(m):
		if not vis[i][j]:
			dfs((i, j), (-1, -1), vis, ans)
print('Yes' if len(ans) else 'No')


enter = input().split()
rows = int(enter[0])
collumns = int(enter[1])
matrix = []
UP = 0
DOWN = 1
RIGHT = 2
LEFT = 3
BEGIN = 4
for i in range(rows):
	row = input()
	matrix.append(row)

def check_cycle(r, c, coordenates, match, move, found, calls):
	if (r, c) in coordenates:
		found.append(1)
	elif calls < 800:
		coordenates.append((r, c))
		if r < rows - 1:
			if matrix[r + 1][c] == match and move != DOWN:
				check_cycle(r + 1, c, coordenates, match, UP, found, calls + 1)
		if c < collumns - 1:
			if matrix[r][c + 1] == match and move != LEFT:
				check_cycle(r, c + 1, coordenates, match, RIGHT, found, calls + 1)
		if r > 0:
			if matrix[r - 1][c] == match and move != UP and (move != BEGIN):
				check_cycle(r - 1, c, coordenates, match, DOWN, found, calls + 1)
		if c > 0:
			if matrix[r][c - 1] == match and move != RIGHT and (move != BEGIN):
				check_cycle(r, c - 1, coordenates, match, LEFT, found, calls + 1)
for r in range(rows):
	for c in range(collumns):
		coordenates = []
		found = []
		calls = 0
		check_cycle(r, c, coordenates, matrix[r][c], BEGIN, found, calls)
		if 1 in found:
			print('Yes')
			break
	if 1 in found:
		break
if not 1 in found:
	print('No')


import sys
sys.setrecursionlimit(10000)

def dfs(x, y, startx, starty, color):
	global visited, board, cycled, numrows, numcols
	xmovement = [1, -1, 0, 0]
	ymovement = [0, 0, 1, -1]
	if x < 0 or x >= numrows or y < 0 or (y >= numcols):
		return cycled
	if board[x][y] != color:
		return cycled
	if cycled == True:
		return cycled
	if visited[x][y]:
		cycled = True
		return cycled
	visited[x][y] = True
	for i in range(4):
		nextx = x + xmovement[i]
		nexty = y + ymovement[i]
		if nextx == startx and nexty == starty:
			continue
		dfs(nextx, nexty, x, y, color)
lines = input().split(' ')
numrows = int(lines[0])
numcols = int(lines[1])
cycled = False
visited = [[False for x in range(numcols)] for x in range(numrows)]
colors = []
board = []
for z in range(numrows):
	colors.append(input())
	board.append(list(colors[z]))
for succX in range(numrows):
	for succY in range(numcols):
		if not visited[succX][succY]:
			dfs(succX, succY, -1, -1, board[succX][succY])
if cycled:
	print('Yes')
else:
	print('No')


from collections import deque
(sizeI, sizeJ) = map(int, input().split())
a = [input() for _ in range(sizeI)]

def bfs(startI, startJ):
	ch = a[startI][startJ]
	visited = [[False for _ in range(sizeJ)] for _ in range(sizeI)]
	visited[startI][startJ] = True
	q = deque([(-1, -1, startI, startJ)])
	while q:
		(parI, parJ, i, j) = q.pop()
		for (di, dj) in ((1, 0), (-1, 0), (0, 1), (0, -1)):
			(ni, nj) = (i + di, j + dj)
			if 0 <= ni < sizeI and 0 <= nj < sizeJ and (a[ni][nj] == ch) and ((ni, nj) != (parI, parJ)):
				if visited[ni][nj]:
					return True
				visited[ni][nj] = True
				q.appendleft((i, j, ni, nj))
	return False
for i in range(sizeI):
	for j in range(sizeJ):
		if bfs(i, j):
			print('Yes')
			exit()
print('No')


from queue import deque
import sys
sys.setrecursionlimit(1000000)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def DFS(g):
	todo = deque(g.vertices())
	reached = {}

	def do_dfs(curr, prev):
		if curr in todo:
			todo.remove(curr)
		if curr in reached:
			return True
		reached[curr] = prev
		for succ in g.neighbours(curr):
			if not succ == prev and do_dfs(succ, curr):
				return True
	while todo:
		reached = {}
		v = todo.popleft()
		if do_dfs(v, v):
			print('Yes')
			return
	print('No')
	return
dots = []
(row, column) = map(int, input().split(' '))
for i in range(row):
	dots.append(input())
g = UndirectedGraph()
for i in range(row):
	for j in range(column):
		if j != column - 1 and dots[i][j] == dots[i][j + 1]:
			g.add_edge(i * column + j, i * column + (j + 1))
		if i != row - 1 and dots[i][j] == dots[i + 1][j]:
			g.add_edge(i * column + j, (i + 1) * column + j)
DFS(g)


I = lambda : list(map(int, input().split()))
(n, m) = I()
g = []
for i in range(n):
	g.append(list(input()))
v = [[0] * m for i in range(n)]

def dfs(x, y, c, pa, py):
	v[x][y] = c
	d = [[-1, 0], [0, -1], [1, 0], [0, 1]]
	for (i, j) in d:
		a = x + i
		b = y + j
		if 0 <= a < n and 0 <= b < m:
			if not v[a][b] and g[a][b] == c:
				dfs(a, b, c, x, y)
			elif v[a][b] == c and a != pa and (b != py):
				print('Yes')
				quit()
c = 1
for i in range(n):
	for j in range(m):
		if not v[i][j]:
			dfs(i, j, g[i][j], -1, -1)
			c += 1
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):
	global 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():
	global 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')


from queue import deque
from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def dfsFindLoop(g, v):
	reached = {}
	foundLoop = False

	def do_dfs(curr, prev, dist):
		nonlocal foundLoop
		if curr == v and dist > 2:
			foundLoop = True
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr, dist + 1)
	do_dfs(v, v, 0)
	return foundLoop

def findLoop(letter, array, n, m):
	g = UndirectedGraph()
	(i, j) = (0, 0)
	while i < n:
		j = 0
		while j < m:
			l = array[i][j]
			if l == letter:
				if i - 1 >= 0 and array[i - 1][j] == l:
					g.add_edge((i, j), (i - 1, j))
				if i + 1 < n and array[i + 1][j] == l:
					g.add_edge((i, j), (i + 1, j))
				if j - 1 >= 0 and array[i][j - 1] == l:
					g.add_edge((i, j), (i, j - 1))
				if j + 1 < m and array[i][j + 1] == l:
					g.add_edge((i, j), (i, j + 1))
			j += 1
		i += 1
	verts = g.vertices()
	for vert in verts:
		if dfsFindLoop(g, vert):
			return True
	return False
(n, m) = map(int, input().split())
array = [list(input()) for x in range(n)]
searchedLetters = {}
(i, j) = (0, 0)
found = False
while i < n and (not found):
	j = 0
	while j < m and (not found):
		letter = array[i][j]
		if letter not in searchedLetters:
			f = findLoop(letter, array, n, m)
			if found == False:
				found = f
			searchedLetters[letter] = 1
		j += 1
	i += 1
if found:
	print('Yes')
else:
	print('No')


I = lambda : int(input())
IL = lambda : list(map(int, input().split()))
(n, m) = IL()
M = ['x' * (m + 2)] + ['x' + input() + 'x' for i in range(n)] + ['x' * (m + 2)]
V = [[int(M[i][j] == 'x') for j in range(m + 2)] for i in range(n + 2)]
for i in range(1, n + 1):
	for j in range(1, m + 1):
		if V[i][j]:
			continue
		V[i][j] = 1
		letter = M[i][j]
		N = [(d_x, d_y) for (d_x, d_y) in [(i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1)] if M[d_x][d_y] == letter]
		while N:
			for (x, y) in N:
				V[x][y] += 1
			N = [(x + d_x, y + d_y) for (x, y) in set(N) for (d_x, d_y) in [(-1, 0), (1, 0), (0, -1), (0, 1)] if M[x + d_x][y + d_y] == letter and V[x + d_x][y + d_y] == 0]
print('No' if max(map(max, V)) == 1 else 'Yes')


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def c_edges(reached):
	edges = 0
	q = set()
	for p in reached:
		curr = g.neighbours(p)
		for x in curr:
			if x not in q:
				edges += 1
		q.add(p)
	return edges

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached
g = UndirectedGraph()
dimension = input().split()
n = int(dimension[0])
m = int(dimension[1])
index = 0
for i in range(n):
	s = input()
	for j in s:
		vertex = j + str(index)
		u_vertex = j + str(index - m)
		p_vertex = j + str(index - 1)
		g.add_vertex(vertex)
		if u_vertex in g.vertices():
			g.add_edge(vertex, u_vertex)
		if p_vertex in g.vertices():
			if index - m * i != 0:
				g.add_edge(vertex, p_vertex)
		index += 1
v = g.vertices()
h = set()
cycle = False
for i in v:
	if i in h:
		continue
	else:
		reached = depth_first_search(g, i)
		if c_edges(reached) != len(reached) - 1:
			cycle = True
			break
		h.update(reached)
if cycle:
	print('Yes')
else:
	print('No')


import sys, os, io

def rs():
	return sys.stdin.readline().rstrip()

def ri():
	return int(sys.stdin.readline())

def ria():
	return list(map(int, sys.stdin.readline().split()))

def ws(s):
	sys.stdout.write(s + '\n')

def wi(n):
	sys.stdout.write(str(n) + '\n')

def wia(a):
	sys.stdout.write(' '.join([str(x) for x in a]) + '\n')
import math, datetime, functools, itertools, operator, bisect, fractions, statistics
from collections import deque, defaultdict, OrderedDict, Counter
from fractions import Fraction
from decimal import Decimal
from sys import stdout
from heapq import heappush, heappop, heapify, _heapify_max, _heappop_max, nsmallest, nlargest
sys.setrecursionlimit(111111)
INF = 99999999999999999999999999999999

def main():
	mod = 1000000007
	starttime = datetime.datetime.now()
	if os.path.exists('input.txt'):
		sys.stdin = open('input.txt', 'r')
		sys.stdout = open('output.txt', 'w')
	tc = 1
	for _ in range(tc):
		(n, m) = ria()
		z = []
		d = {}
		for i in range(n):
			z.append(list(rs()))
		for i in range(n):
			for j in range(m):
				col = z[i][j]
				if col in d:
					d[col].append([i, j])
				else:
					d[col] = [[i, j]]
		vis = [[0 for i in range(m)] for j in range(n)]

		def dfs(i, j, pi, pj, cold):
			if vis[i][j] == -1:
				print('Yes')
				quit()
			else:
				vis[i][j] = -1
				if (i + 1 != pi or j != pj) and [i + 1, j] in cold:
					dfs(i + 1, j, i, j, cold)
				if (i - 1 != pi or j != pj) and [i - 1, j] in cold:
					dfs(i - 1, j, i, j, cold)
				if (i != pi or j + 1 != pj) and [i, j + 1] in cold:
					dfs(i, j + 1, i, j, cold)
				if (i != pi or j - 1 != pj) and [i, j - 1] in cold:
					dfs(i, j - 1, i, j, cold)
			vis[i][j] = 1
		for i in d:
			for (x, y) in d[i]:
				if not vis[x][y]:
					dfs(x, y, -1, -1, d[i])
		print('No')
	endtime = datetime.datetime.now()
	time = (endtime - starttime).total_seconds() * 1000
	if os.path.exists('input.txt'):
		print('Time:', time, 'ms')

class FastReader(io.IOBase):
	newlines = 0

	def __init__(self, fd, chunk_size=1024 * 8):
		self._fd = fd
		self._chunk_size = chunk_size
		self.buffer = io.BytesIO()

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, self._chunk_size))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self, size=-1):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, self._chunk_size if size == -1 else size))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

class FastWriter(io.IOBase):

	def __init__(self, fd):
		self._fd = fd
		self.buffer = io.BytesIO()
		self.write = self.buffer.write

	def flush(self):
		os.write(self._fd, self.buffer.getvalue())
		(self.buffer.truncate(0), self.buffer.seek(0))

class FastStdin(io.IOBase):

	def __init__(self, fd=0):
		self.buffer = FastReader(fd)
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')

class FastStdout(io.IOBase):

	def __init__(self, fd=1):
		self.buffer = FastWriter(fd)
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.flush = self.buffer.flush
sys.stdin = FastStdin()
sys.stdout = FastStdout()
main()


from sys import stdin, stdout
lines = stdin.readlines()
(n, m) = (int(lines[0].split()[0]), int(lines[0].split()[1]))
a = [[x[i] for i in range(m)] for x in lines[1:]]

def get_all_connections(a, i, j):
	edges = []
	if j > 0:
		if a[i][j] == a[i][j - 1]:
			edges.append(str([i, j - 1]))
	if j < m - 1:
		if a[i][j] == a[i][j + 1]:
			edges.append(str([i, j + 1]))
	if i > 0:
		if a[i][j] == a[i - 1][j]:
			edges.append(str([i - 1, j]))
	if i < n - 1:
		if a[i][j] == a[i + 1][j]:
			edges.append(str([i + 1, j]))
	return set(edges)
graph = {str([i, j]): get_all_connections(a, i, j) for i in range(n) for j in range(m)}
graph['cycle'] = 'cycle'

def dfs(graph, start, prev=None, exit_now=0, visited=None):
	if visited is None:
		visited = set()
	visited.add(start)
	if prev is None:
		prev = set()
	for next in graph[start] - prev:
		if exit_now == 1:
			break
		if next in visited:
			visited.add(next)
			visited.add('cycle')
			exit_now = 1
		else:
			prev = set()
			prev.add(start)
			dfs(graph, next, prev.copy(), exit_now, visited)
	return visited

def check_square(a, i, j):
	if a[i][j] == a[i][j + 1] and a[i][j] == a[i + 1][j] and (a[i][j] == a[i + 1][j + 1]):
		return True

def check_simple_case(a, n, m):
	if n > 2 and m > 2:
		for i in range(0, n - 2):
			for j in range(0, m - 2):
				if check_square(a, i, j):
					return True
		return False
	else:
		return False
	return False
flag = 1
if check_simple_case(a, n, m):
	flag = 0
for i in range(n):
	if flag == 0:
		break
	for j in range(m):
		if flag == 0:
			break
		path_graph = dfs(graph, str([i, j]))
		if 'cycle' in path_graph:
			flag = 0
		else:
			for elem in path_graph:
				graph[elem] = set()
if flag == 0:
	print('Yes')
else:
	print('No')


import sys
sys.setrecursionlimit(10000)
num_of_input = input().split(' ')
num_of_rows = int(num_of_input[0])
num_of_columns = int(num_of_input[1])
g = []
visited = []

def depth_first_search(x, y, start_x, start_y, color):
	global find_cycle
	if x < 0 or x >= num_of_rows or y < 0 or (y >= num_of_columns):
		return
	if g[x][y] != color:
		return
	if visited[x][y]:
		find_cycle = 'Yes'
		return
	direct = [1, -1, 0, 0]
	visited[x][y] = True
	for i in range(4):
		next_x = x + direct[i]
		next_y = y + direct[-i - 1]
		if next_x == start_x and next_y == start_y:
			continue
		depth_first_search(next_x, next_y, x, y, color)
find_cycle = 'No'
for i in range(num_of_rows):
	cell_input = input()
	g.append(cell_input)
	visited.append([False] * num_of_columns)
for i in range(num_of_rows):
	for j in range(num_of_columns):
		if not visited[i][j]:
			depth_first_search(i, j, -1, -1, g[i][j])
print(find_cycle)


(n, m) = list(map(int, input().split()))
nodes = [list(input()) for i in range(n)]
parent = [i for i in range(n * m)]

def find(i):
	if i != parent[i]:
		parent[i] = find(parent[i])
	return parent[i]

def union(i, j):
	(pi, pj) = (find(i), find(j))
	if pi != pj:
		parent[pi] = pj

def getId(i, j):
	return i * m + j
for i in range(n):
	for j in range(m):
		if i + 1 < n and nodes[i][j] == nodes[i + 1][j]:
			if find(getId(i, j)) == find(getId(i + 1, j)):
				print('Yes')
				exit()
			union(getId(i, j), getId(i + 1, j))
		if j + 1 < m and nodes[i][j] == nodes[i][j + 1]:
			if find(getId(i, j)) == find(getId(i, j + 1)):
				print('Yes')
				exit()
			union(getId(i, j), getId(i, j + 1))
print('No')


import sys
sys.setrecursionlimit(10 ** 5)
from collections import deque
(n, m) = map(int, input().split())
cor = []
grid = []
g = []
for i in range(n):
	grid.append(input())
	g.append([])
	cor.append([0] * m)
	for _ in range(m):
		g[i].append([])
for i in range(n):
	for j in range(m):
		if i and grid[i][j] == grid[i - 1][j]:
			g[i][j].append((i - 1, j))
		if j and grid[i][j] == grid[i][j - 1]:
			g[i][j].append((i, j - 1))
		if i < n - 1 and grid[i][j] == grid[i + 1][j]:
			g[i][j].append((i + 1, j))
		if j < m - 1 and grid[i][j] == grid[i][j + 1]:
			g[i][j].append((i, j + 1))

def achaCiclo(i, j, pre):
	cor[i][j] = 1
	for v in g[i][j]:
		if v == pre:
			continue
		if cor[v[0]][v[1]] == 0:
			achaCiclo(v[0], v[1], (i, j))
		if cor[v[0]][v[1]] in [1, 3]:
			cor[i][j] = 3
	if cor[i][j] == 1:
		cor[i][j] = 2
ciclo = 'No'
for i in range(n):
	for j in range(m):
		if cor[i][j] == 0:
			achaCiclo(i, j, (-1, -1))
			if cor[i][j] == 3:
				ciclo = 'Yes'
				break
print(ciclo)


from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc
(n, m) = map(int, input().split())
l = []
for i in range(n):
	l.append(list(input()))
visited = [[0] * m for i in range(n)]
f = True

@bootstrap
def dfs(y, x, oldy, oldx, ch):
	global f
	if f:
		if visited[y][x] != 0:
			yield
		if l[y][x] != ch:
			yield
		visited[y][x] = ch
		if 0 <= y + 1 < n and 0 <= x < m and f:
			if (y + 1, x) != (oldy, oldx) and visited[y + 1][x] == ch and (l[y + 1][x] == ch):
				print('Yes')
				f = False
			yield dfs(y + 1, x, y, x, ch)
		if 0 <= y - 1 < n and 0 <= x < m and f:
			if (y - 1, x) != (oldy, oldx) and visited[y - 1][x] == ch and (l[y - 1][x] == ch):
				print('Yes')
				f = False
			yield dfs(y - 1, x, y, x, ch)
		if 0 <= y < n and 0 <= x + 1 < m and f:
			if (y, x + 1) != (oldy, oldx) and visited[y][x + 1] == ch and (l[y][x + 1] == ch):
				print('Yes')
				f = False
			yield dfs(y, x + 1, y, x, ch)
		if 0 <= y < n and 0 <= x - 1 < m and f:
			if (y, x - 1) != (oldy, oldx) and visited[y][x - 1] == ch and (l[y][x - 1] == ch):
				print('Yes')
				f = False
			yield dfs(y, x - 1, y, x, ch)
	yield
for y in range(n):
	for x in range(m):
		if f:
			if visited[y][x] == 0:
				dfs(y, x, -1, -1, l[y][x])
		else:
			break
if f:
	print('No')


[n, m] = [int(i) for i in input().split()]
visit = [[] for i in range(n)]
for i in range(n):
	str = input()
	for j in str:
		visit[i].append(ord(j))

def find(sr, st, en, pat):
	for i in range(st, en + 1, 1):
		if visit[sr][i] != pat:
			return 0
	return 1
for i in range(n):
	for j in range(m):
		k = j + 1
		while k < m and visit[i][j] == visit[i][k]:
			l = i + 1
			while l < n and visit[l][k] == visit[i][j]:
				if find(l, j, k, visit[i][j]):
					print('Yes')
					exit(0)
				l += 1
			k += 1
print('No')


(rows, cols) = [int(i) for i in input().split(' ')]
s = list()
for _ in range(rows):
	s.append(list(input()))
a = [[False for _ in range(cols)] for _ in range(rows)]
for r in range(rows):
	for c in range(cols):
		if not a[r][c]:
			p = [[r, c]]
			d = [-1, 0]
			a[r][c] = True
			string = s[r][c]
			while p:
				(row, col) = p[-1]
				direction = d[-1]
				if direction == 4:
					del p[-1]
					del d[-1]
					d[-1] += 1
				elif direction == 3 - d[-2]:
					d[-1] += 1
				else:
					if direction == 0:
						row -= 1
					elif direction == 1:
						col -= 1
					elif direction == 2:
						col += 1
					elif direction == 3:
						row += 1
					if 0 <= row < rows and 0 <= col < cols and (string == s[row][col]):
						if a[row][col]:
							print('Yes')
							exit()
						else:
							a[row][col] = True
							p.append([row, col])
							d.append(0)
					else:
						d[-1] += 1
print('No')


import sys
(x, y) = map(int, input().split())
sys.setrecursionlimit(x * y + 10)
value = []
for _ in range(x):
	value.append(tuple(input()))
visited = [[False] * y for _ in range(x)]

def visit(i, j, npath, previ, prevj):
	global visited, x, y, value
	npath.add((i, j))
	visited[i][j] = True
	if i - 1 >= 0 and value[i][j] == value[i - 1][j]:
		if (i - 1, j) in npath and len(npath) > 3 and (i - 1 != previ) and (j != prevj):
			print('Yes')
			exit()
		elif not visited[i - 1][j]:
			visit(i - 1, j, npath, i, j)
	if i + 1 < x and value[i][j] == value[i + 1][j]:
		if (i + 1, j) in npath and len(npath) > 3 and (i + 1 != previ) and (j != prevj):
			print('Yes')
			exit()
		elif not visited[i + 1][j]:
			visit(i + 1, j, npath, i, j)
	if j - 1 >= 0 and value[i][j] == value[i][j - 1]:
		if (i, j - 1) in npath and len(npath) > 3 and (i != previ) and (j - 1 != prevj):
			print('Yes')
			exit()
		elif not visited[i][j - 1]:
			visit(i, j - 1, npath, i, j)
	if j + 1 < y and value[i][j] == value[i][j + 1]:
		if (i, j + 1) in npath and len(npath) > 3 and (i != previ) and (j + 1 != prevj):
			print('Yes')
			exit()
		elif not visited[i][j + 1]:
			visit(i, j + 1, npath, i, j)
for i in range(x):
	for j in range(y):
		if not visited[i][j]:
			visit(i, j, set(), -1, -1)
print('No')


def dfs(x, y, num):
	global visit
	visit[x][y] = num
	for k in range(4):
		x1 = x + dx[k]
		y1 = y + dy[k]
		if 0 <= x1 < n and 0 <= y1 < m:
			if dots[x][y] == dots[x1][y1]:
				if visit[x1][y1] == 0:
					dfs(x1, y1, num + 1)
				if abs(visit[x][y] - visit[x1][y1]) >= 3:
					print('Yes')
					quit()
(n, m) = map(int, input().split())
dots = [list(input()) for i in range(n)]
visit = [[0] * m for i in range(n)]
dx = [0, -1, 0, 1]
dy = [1, 0, -1, 0]
for i in range(n):
	for j in range(m):
		if visit[i][j] == 0:
			dfs(i, j, 1)
print('No')


import sys
sys.setrecursionlimit(10000)

def is_valid_position(next_i, next_j, i, j):
	if not 0 <= next_i < n or not 0 <= next_j < m:
		return False
	if matrix[i][j] != matrix[next_i][next_j]:
		return False
	if i == next_i and j == next_j:
		return False
	return True

def dfs(i, j, root_i, root_j, visited):
	if visited[i][j]:
		return True
	visited[i][j] = 1
	for (row, column) in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
		next_i = i + row
		next_j = j + column
		if is_valid_position(next_i, next_j, root_i, root_j) and dfs(next_i, next_j, i, j, visited):
			return True
	return False

def has_cycle():
	visited = [[0 for i in range(m)] for j in range(n)]
	for i in range(n):
		for j in range(m):
			if not visited[i][j]:
				if dfs(i, j, i, j, visited):
					return True
	return False
[n, m] = list(map(int, input().split()))
matrix = []
for i in range(n):
	matrix.append(list(input()))
if has_cycle():
	print('Yes')
else:
	print('No')
teste = False


def findCycle(color, xant, yant, i, j):
	visited[i][j] = True
	if i < rows - 1 and color == board[i + 1][j] and (xant != i + 1 or yant != j):
		if not visited[i + 1][j]:
			findCycle(color, i, j, i + 1, j)
		else:
			return True
	if i > 0 and color == board[i - 1][j] and (xant != i - 1 or yant != j):
		if not visited[i - 1][j]:
			findCycle(color, i, j, i - 1, j)
		else:
			return True
	if j > 0 and color == board[i][j - 1] and (xant != i or yant != j - 1):
		if not visited[i][j - 1]:
			findCycle(color, i, j, i, j - 1)
		else:
			return True
	if j < cols - 1 and color == board[i][j + 1] and (xant != i or yant != j + 1):
		if not visited[i][j + 1]:
			findCycle(color, i, j, i, j + 1)
		else:
			return True
	return False
(rows, cols) = map(int, input().split())
board = [[] for i in range(rows)]
visited = [[False for i in range(cols + 2)] for j in range(rows + 2)]
for i in range(rows):
	linha = input()
	for j in range(cols):
		board[i].append(linha[j])
ans = 'No'
for i in range(rows):
	for j in range(cols):
		visited = [[False for i in range(cols + 2)] for j in range(rows + 2)]
		color = board[i][j]
		if findCycle(color, -1, -1, i, j):
			ans = 'Yes'
			break
print(ans)


import sys
sys.setrecursionlimit(10000)
FOUND = False
(n, m) = map(int, input().split())
g = []
visited = [[False for j in range(m)] for i in range(n)]
for i in range(n):
	g.append(input())

def solution(g, i, j, x, y):
	global FOUND
	global calls
	if i > len(g) or j > len(g[0]):
		return
	if visited[i][j]:
		FOUND = True
		return
	visited[i][j] = True
	if i + 1 < len(g) and g[i + 1][j] == g[i][j] and (i + 1 != x or j != y):
		solution(g, i + 1, j, i, j)
	if i - 1 >= 0 and g[i - 1][j] == g[i][j] and (i - 1 != x or j != y):
		solution(g, i - 1, j, i, j)
	if j + 1 < len(g[0]) and g[i][j + 1] == g[i][j] and (i != x or j + 1 != y):
		solution(g, i, j + 1, i, j)
	if j - 1 >= 0 and g[i][j - 1] == g[i][j] and (i != x or j - 1 != y):
		solution(g, i, j - 1, i, j)
for i in range(len(g)):
	for j in range(len(g[i])):
		if not visited[i][j]:
			solution(g, i, j, i, j)
print('Yes' if FOUND else 'No')


import sys, math
input = sys.stdin.readline

def getInts():
	return [int(s) for s in input().split()]

def getInt():
	return int(input())

def getStrs():
	return [s for s in input().split()]

def getStr():
	return input().strip()

def listStr():
	return list(input().strip())
import collections as col
import math, itertools

def solve():

	def bfs(graph, visited, node, queue, nodes):
		visited.add(node)
		nodes.remove(node)
		queue.append((node, None))
		queue = col.deque(queue)
		cycle = False
		while queue:
			(s, parent) = queue.popleft()
			for neighbour in graph[s]:
				if neighbour in visited and neighbour != parent:
					cycle = True
				elif neighbour not in visited:
					visited.add(neighbour)
					nodes.remove(neighbour)
					queue.append((neighbour, s))
		return (cycle, nodes)
	(N, M) = getInts()
	grid = []
	graph = col.defaultdict(set)
	for n in range(N):
		grid.append(listStr())
	for n in range(N):
		for m in range(M):
			targ = grid[n][m]
			if n > 0 and grid[n - 1][m] == targ:
				graph[n, m].add((n - 1, m))
			if n < N - 1 and grid[n + 1][m] == targ:
				graph[n, m].add((n + 1, m))
			if m > 0 and grid[n][m - 1] == targ:
				graph[n, m].add((n, m - 1))
			if m < M - 1 and grid[n][m + 1] == targ:
				graph[n, m].add((n, m + 1))
	nodes = set([(n, m) for (n, m) in itertools.product(range(N), range(M))])
	connected_components = []
	while nodes:
		node = next(iter(nodes))
		(ans, nodes) = bfs(graph, set(), node, [], nodes)
		if ans:
			return 'Yes'
	return 'No'
print(solve())


import sys

def input():
	return sys.stdin.readline().strip()
(n, m) = map(int, input().split())
adj = [[[] for j in range(m)] for i in range(n)]
val = []
for _ in range(n):
	s = input()
	value = list(s)
	val.append(value)
for i in range(0, n - 1):
	for j in range(0, m - 1):
		adj[i][j].append([i, j + 1])
		adj[i][j + 1].append([i, j])
		adj[i + 1][j].append([i, j])
		adj[i][j].append([i + 1, j])
for i in range(0, n - 1):
	adj[i][m - 1].append([i + 1, m - 1])
	adj[i + 1][m - 1].append([i, m - 1])
for i in range(0, m - 1):
	adj[n - 1][i].append([n - 1, i + 1])
	adj[n - 1][i + 1].append([n - 1, i])

def dfs(i, j, vis, adj, val, n, m):
	stack = [[i, j]]
	pi = i
	pj = j
	parent = [[[-1, -1] for jj in range(m)] for ii in range(n)]
	while len(stack) > 0:
		u = stack[-1]
		i = u[0]
		j = u[1]
		if vis[i][j] == 0:
			vis[i][j] = 1
			vi = adj[i][j]
			f = 0
			ff = 0
			for v in vi:
				(newi, newj) = (v[0], v[1])
				if val[i][j] == val[newi][newj] and vis[newi][newj] == 0:
					f = 1
					stack.append([newi, newj])
					parent[newi][newj] = [i, j]
				if pi == newi and pj == newj and (parent[i][j] != [pi, pj]):
					ff = 1
			if f == 0 and ff == 1:
				return 1
		else:
			stack.pop()
	return 0
flag = 0
for i in range(0, n):
	for j in range(0, m):
		vis = [[0 for jj in range(m)] for ii in range(n)]
		cycle = dfs(i, j, vis, adj, val, n, m)
		if cycle == 1:
			flag = 1
			break
	if flag == 1:
		break
if cycle == 1:
	print('Yes')
else:
	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')


import sys

def adde(u, v):
	global graph
	graph[u].append(v)

def read_data():
	global n, m, graph
	s = input().split()
	m = int(s[0])
	n = int(s[1])
	graph = [[] for u in range(n * m)]
	s = [list(input()) for i in range(m)]
	for i in range(m):
		for j in range(n):
			if i + 1 < m and s[i][j] == s[i + 1][j]:
				adde(i * n + j, (i + 1) * n + j)
				adde((i + 1) * n + j, i * n + j)
			if j + 1 < n and s[i][j] == s[i][j + 1]:
				adde(i * n + j, i * n + j + 1)
				adde(i * n + j + 1, i * n + j)

def DFS(u, par_u):
	global visited, res
	visited.append(u)
	for v in graph[u]:
		if not v == par_u:
			if v in visited:
				res = True
				return
			else:
				DFS(v, u)
				if res == True:
					return
sys.setrecursionlimit(1500)
graph = []
read_data()
res = False
visited = []
for u in range(n * m):
	if u not in visited:
		print
		DFS(u, -1)
		if res == True:
			break
if res == True:
	print('Yes')
else:
	print('No')


import sys
(N, M) = input().split()
(N, M) = (int(N), int(M))
fields = list()
for i in range(N):
	fields.append(str(input()))
graph = list()
for i in range(N):
	graph.append([[fields[i][j], 0] for j in range(M)])
ALL_MOVES = [[0, 1], [1, 0], [-1, 0], [0, -1]]

def good_move(a, b, parent):
	return a >= 0 and b >= 0 and (a < N) and (b < M) and (b != parent[1] or a != parent[0])

def dfs(node, parent):
	graph[node[0]][node[1]][1] = 1
	for move in ALL_MOVES:
		nm0 = node[0] + move[0]
		nm1 = node[1] + move[1]
		if good_move(nm0, nm1, parent) and graph[node[0]][node[1]][0] == graph[nm0][nm1][0]:
			if graph[nm0][nm1][1] == 1:
				return True
			if dfs([nm0, nm1], node):
				return True
	return False
for i in range(N):
	for j in range(M):
		if graph[i][j][1] == 0:
			if dfs([i, j], [-1, -1]):
				print('Yes')
				sys.exit(0)
print('No')


import sys
sys.setrecursionlimit(3000)
IN = input().split()
n = int(IN[0])
m = int(IN[1])
A = [0] * n
used = [0] * n
par = [0] * n
DX = [0, 0, 1, -1]
DY = [1, -1, 0, 0]
ans = 'No'

def inside(x, y):
	return min(x, y) >= 0 and x < n and (y < m)

def dfs(x, y):
	used[x][y] = 1
	for i in range(0, 4):
		cx = x + DX[i]
		cy = y + DY[i]
		if inside(cx, cy) and A[cx][cy] == A[x][y]:
			if used[cx][cy] == 0:
				par[cx][cy] = [x, y]
				dfs(cx, cy)
			elif used[cx][cy] == 1 and par[x][y] != [cx, cy]:
				print('Yes')
				exit(0)
	used[x][y] = 2
for i in range(0, n):
	A[i] = input()
	used[i] = [0] * m
	par[i] = [0, 0] * m
for i in range(0, n):
	for j in range(0, m):
		if used[i][j] == 0:
			dfs(i, j)
print('No')


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

def check_cycle(x, y):
	visited = [[0 for i in range(m)] for j in range(n)]
	color = grid[x][y]
	cur = [[[x, -1], [y, -1]]]
	while len(cur) > 0:
		ele = cur.pop()
		i = ele[0][0]
		j = ele[1][0]
		prev_x = ele[0][1]
		prev_y = ele[1][1]
		if visited[i][j]:
			return True
		visited[i][j] = 1
		if i + 1 < n and (prev_x != i + 1 or prev_y != j) and (grid[i + 1][j] == color):
			cur.append([[i + 1, i], [j, j]])
		if j + 1 < m and (prev_x != i or prev_y != j + 1) and (grid[i][j + 1] == color):
			cur.append([[i, i], [j + 1, j]])
		if i - 1 >= 0 and (prev_x != i - 1 or prev_y != j) and (grid[i - 1][j] == color):
			cur.append([[i - 1, i], [j, j]])
		if j - 1 >= 0 and (prev_x != i or prev_y != j - 1) and (grid[i][j - 1] == color):
			cur.append([[i, i], [j - 1, j]])
	return False
cycle = False
for i in range(n - 1):
	for j in range(m - 1):
		if check_cycle(i, j):
			cycle = True
			break
	if cycle:
		break
if cycle:
	print('Yes')
else:
	print('No')


import sys
sys.setrecursionlimit(10000)

def iin(x, y):
	return 0 <= x < n and 0 <= y < m

def dfs(x, y, fax=-1, fay=-1):
	V[x][y] = True
	dx = [1, -1, 0, 0]
	dy = [0, 0, 1, -1]
	for i in range(4):
		xx = x + dx[i]
		yy = y + dy[i]
		if iin(xx, yy) and M[x][y] == M[xx][yy]:
			if V[xx][yy] and (not (xx == fax and yy == fay)):
				print('Yes')
				sys.exit(0)
			if not V[xx][yy]:
				dfs(xx, yy, x, y)
(n, m) = map(int, input().split())
M = [input() for _ in range(n)]
V = [[False] * m for _ in range(n)]
for i in range(n):
	for j in range(m):
		if not V[i][j]:
			dfs(i, j)
print('No')


def main():
	bounds = input().split(' ')
	a = int(bounds[0])
	b = int(bounds[1])
	board = []
	visited = set()
	for i in range(a):
		board.append(input())
	for i in range(a):
		for j in range(b):
			if (i, j) not in visited:
				aux = cycle(i, j, board, a, b)
				if aux:
					visited = visited | aux
				else:
					print('Yes')
					return
	print('No')

def adjacent(vertex, color, board, a, b):
	aux = []
	(x, y) = vertex
	if x > 0:
		if board[x - 1][y] == color:
			aux.append((x - 1, y))
	if y > 0:
		if board[x][y - 1] == color:
			aux.append((x, y - 1))
	if x < a - 1:
		if board[x + 1][y] == color:
			aux.append((x + 1, y))
	if y < b - 1:
		if board[x][y + 1] == color:
			aux.append((x, y + 1))
	return aux

def cycle(x, y, board, a, b):
	color = board[x][y]
	visited = set()
	stack = [((x, y), None)]
	while stack:
		(vertex, parent) = stack.pop()
		if vertex in visited:
			return set()
		visited.add(vertex)
		for value in adjacent(vertex, color, board, a, b):
			if value != parent:
				stack.append((value, vertex))
	return visited
main()


import math, string, itertools, fractions, heapq, collections, re, array, bisect, sys, copy, functools
sys.setrecursionlimit(10 ** 7)
inf = 10 ** 20
eps = 1.0 / 10 ** 10
mod = 998244353
dd = [(-1, 0), (0, 1), (1, 0), (0, -1)]
ddn = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)]

def LI():
	return list(map(int, sys.stdin.readline().split()))

def LLI():
	return [list(map(int, l.split())) for l in sys.stdin.readlines()]

def LI_():
	return [int(x) - 1 for x in sys.stdin.readline().split()]

def LF():
	return [float(x) for x in sys.stdin.readline().split()]

def LS():
	return sys.stdin.readline().split()

def I():
	return int(sys.stdin.readline())

def F():
	return float(sys.stdin.readline())

def S():
	return input()

def pf(s):
	return print(s, flush=True)

def pe(s):
	return print(str(s), file=sys.stderr)

def JA(a, sep):
	return sep.join(map(str, a))

def JAA(a, s, t):
	return s.join((t.join(map(str, b)) for b in a))

def main():
	(n, m) = LI()
	aa = [S() for _ in range(n)]
	v = collections.defaultdict(int)

	def f(i, j, k, t):
		if v[i, j] > 0:
			return k - v[i, j] > 3
		v[i, j] = k
		for (di, dj) in dd:
			ni = i + di
			nj = j + dj
			if ni < 0 or ni >= n or nj < 0 or (nj >= m):
				continue
			if aa[ni][nj] != t:
				continue
			if f(ni, nj, k + 1, t):
				return True
		return False
	for i in range(n):
		for j in range(m):
			if v[i, j] > 0:
				continue
			if f(i, j, 1, aa[i][j]):
				return 'Yes'
	return 'No'
print(main())


import sys
sys.setrecursionlimit(10000)

def hasCycle(dots, point=[0, 0], point_parent=None):
	(i, j) = point
	dots[i][j]['visited'] = True
	n = len(dots)
	m = len(dots[i])
	for x in range(4):
		if x == 0 and i > 0:
			if dots[i][j]['color'] == dots[i - 1][j]['color']:
				if not dots[i - 1][j]['visited']:
					if hasCycle(dots, [i - 1, j], [i, j]):
						return True
				elif point_parent != [i - 1, j]:
					return True
		if x == 1 and j < m - 1:
			if dots[i][j]['color'] == dots[i][j + 1]['color']:
				if not dots[i][j + 1]['visited']:
					if hasCycle(dots, [i, j + 1], [i, j]):
						return True
				elif point_parent != [i, j + 1]:
					return True
		if x == 2 and i < n - 1:
			if dots[i][j]['color'] == dots[i + 1][j]['color']:
				if not dots[i + 1][j]['visited']:
					if hasCycle(dots, [i + 1, j], [i, j]):
						return True
				elif point_parent != [i + 1, j]:
					return True
		if x == 3 and j > 0:
			if dots[i][j]['color'] == dots[i][j - 1]['color']:
				if not dots[i][j - 1]['visited']:
					if hasCycle(dots, [i, j - 1], [i, j]):
						return True
				elif point_parent != [i, j - 1]:
					return True
	return False
(n, m) = [int(n) for n in input().split(' ')]
dots = []
for i in range(n):
	points = [p for p in input()]
	dots.append(points)
dots = [[{'color': p, 'visited': False} for p in line] for line in dots]
result = False
i = j = 0
while not result and i < n:
	while not result and j < m:
		if not dots[i][j]['visited'] and hasCycle(dots, [i, j]):
			result = True
		j += 1
	i += 1
	j = 0
print('Yes' if result else 'No')


from queue import deque
from sys import setrecursionlimit

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search_modified(g):
	todo = deque(g.vertices())
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return True
		reached[curr] = prev
		for succ in g.neighbours(curr):
			if not succ == prev and do_dfs(succ, curr):
				return True
	while todo:
		reached = {}
		v = todo.popleft()
		if do_dfs(v, v):
			print('Yes')
			return
	print('No')
	return

def MatrixGenerate():
	line_1 = input()
	m = int(line_1.split()[0])
	n = int(line_1.split()[1])
	matrix = [[0 for i in range(n)] for j in range(m)]
	for a in range(m):
		line_2 = input()
		for b in range(n):
			matrix[a][b] = line_2[b]
	return [matrix, m, n]

def GraphStore(matrix, m, n):
	g = Graph()
	for a in range(m):
		for b in range(n):
			g.add_vertex(a * n + b)
	for a in range(m):
		for b in range(n):
			if not a < 1:
				if matrix[a][b] == matrix[a - 1][b]:
					g.add_edge(a * n + b, (a - 1) * n + b)
			if not a > m - 2:
				if matrix[a][b] == matrix[a + 1][b]:
					g.add_edge(a * n + b, (a + 1) * n + b)
			if not b < 1:
				if matrix[a][b] == matrix[a][b - 1]:
					g.add_edge(a * n + b, a * n + b - 1)
			if not b > n - 2:
				if matrix[a][b] == matrix[a][b + 1]:
					g.add_edge(a * n + b, a * n + b + 1)
	return g
setrecursionlimit(1000000)
matrix = MatrixGenerate()
g = GraphStore(matrix[0], matrix[1], matrix[2])
depth_first_search_modified(g)


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def is_cycle(g, coordinates):
	marked = {v: False for v in coordinates}
	queue = []
	for vertex in coordinates:
		if marked[vertex] == True:
			continue
		else:
			queue.append((vertex, vertex))
		while queue:
			item = queue.pop()
			current = item[0]
			prev = item[1]
			if marked[current] == False:
				marked[current] = True
			for i in g.neighbours(current):
				if marked[i] == False:
					marked[i] = True
					queue.append((i, current))
				elif i != prev and marked[i] == True:
					return True
	return False
(num_rows, num_columns) = map(int, input().split(' '))
grid = UndirectedGraph()
dots = {}
for n in range(num_rows):
	row_input = input()
	for m in range(num_columns):
		grid.add_vertex((n, m))
		dots[n, m] = row_input[m]
		if m > 0 and dots[n, m - 1] is row_input[m]:
			grid.add_edge((n, m - 1), (n, m))
		if n > 0 and dots[n - 1, m] is row_input[m]:
			grid.add_edge((n - 1, m), (n, m))
coordinates = sorted(dots.keys())
cycle_check = is_cycle(grid, coordinates)
if cycle_check == True:
	print('Yes')
else:
	print('No')


import sys

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def hasCycle(g):
	checked = set()

	def visit(g, curr, leng, init, seen):
		seen |= {curr}
		for n in g.neighbours(curr):
			if n not in seen and len(g.neighbours(n)) >= 2:
				if visit(g, n, leng + 1, init, seen):
					return True
			if n == init and leng > 3:
				return True
	for v in g.vertices():
		if v not in checked:
			checked |= {v}
			if len(g.neighbours(v)) >= 2:
				seen = {v}
				if visit(g, v, 1, v, seen):
					return True
	return False
sys.setrecursionlimit(10000)
colours = {}
seen = set()
size = input().split()
for i in range(int(size[0])):
	line = list(input())
	for j in range(int(size[1])):
		c = line[j]
		if c not in seen:
			seen |= {c}
			colours[c] = UndirectedGraph()
		colours[c].add_vertex(str(i) + ' ' + str(j))
		if str(i - 1) + ' ' + str(j) in colours[c].vertices():
			colours[c].add_edge(str(i - 1) + ' ' + str(j), str(i) + ' ' + str(j))
		if str(i) + ' ' + str(j - 1) in colours[c].vertices():
			colours[c].add_edge(str(i) + ' ' + str(j - 1), str(i) + ' ' + str(j))
NoCycle = True
for c in seen:
	if hasCycle(colours[c]):
		print('Yes')
		NoCycle = False
		break
if NoCycle == True:
	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')


import os
import sys
from io import BytesIO, IOBase
import math
from decimal import Decimal
from decimal import *
from collections import defaultdict, deque
import heapq
from decimal import Decimal
getcontext().prec = 25
abcd = 'abcdefghijklmnopqrstuvwxyz'
MOD = pow(10, 9) + 7
BUFSIZE = 8192
from bisect import bisect_left, bisect_right

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def dfs(i, j):
	val = l[i][j]
	s1 = set()
	s2 = set()
	q = deque()
	q.append([i, j])
	b = 0
	while q:
		x = q.popleft()
		if not v[x[0]][x[1]]:
			v[x[0]][x[1]] = True
			s2.add((x[0], x[1]))
			if l[x[0] + 1][x[1]] == val:
				q.append([x[0] + 1, x[1]])
				b += 1
			if l[x[0]][x[1] + 1] == val:
				q.append([x[0], x[1] + 1])
				b += 1
			if l[x[0] - 1][x[1]] == val:
				q.append([x[0] - 1, x[1]])
				b += 1
			if l[x[0]][x[1] - 1] == val:
				q.append([x[0], x[1] - 1])
				b += 1
	if len(s2) <= b // 2:
		return True
	return False
(n, m) = map(int, input().split(' '))
l = []
l.append('=' * (m + 2))
for i in range(n):
	l.append('=' + input() + '=')
l.append('=' * (m + 2))
v = [[False for i in range(m + 2)] for j in range(n + 2)]
ans = False
for i in range(n + 2):
	for j in range(m + 2):
		if l[i][j] == '=':
			v[i][j] = True
			continue
		if not v[i][j]:
			zz = dfs(i, j)
			if zz:
				ans = True
				break
	if ans:
		break
if ans:
	print('Yes')
else:
	print('No')


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)
minimumCycleLen = 4

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def is_cycle_helper(g):
	reached = set()

	def is_cycle(g, vertex, cycle):
		theNeighbours = g.neighbours(vertex)
		cycle.append(vertex)
		if len(cycle) >= minimumCycleLen and cycle[0] in theNeighbours:
			return True
		for neighbour in theNeighbours:
			if neighbour not in cycle and neighbour not in reached:
				if is_cycle(g, neighbour, cycle) == False:
					reached.add(neighbour)
					cycle.remove(neighbour)
				else:
					return True
		else:
			return False
	for v in g._alist:
		if v not in reached:
			if is_cycle(g, v, list()):
				print('Yes')
				break
			reached.add(v)
	else:
		print('No')
g = UndirectedGraph()
nums = input().split()
n = int(nums[0])
m = int(nums[1])
listn = list(range(n))
listm = list(range(m))
matrix = [[y for y in listm] for x in listn]
for rowIdx in range(n):
	columnIdx = 0
	line = input()
	for letter in line:
		g.add_vertex(str((rowIdx, columnIdx)))
		matrix[rowIdx][columnIdx] = letter
		if rowIdx - 1 >= 0 and matrix[rowIdx - 1][columnIdx] == letter:
			g.add_edge(str((rowIdx - 1, columnIdx)), str((rowIdx, columnIdx)))
		if columnIdx - 1 >= 0 and matrix[rowIdx][columnIdx - 1] == letter:
			g.add_edge(str((rowIdx, columnIdx - 1)), str((rowIdx, columnIdx)))
		columnIdx += 1
is_cycle_helper(g)


import sys
sys.setrecursionlimit(10000)
(rows, columns) = map(int, input().split())
visited = [[False for i in range(columns)] for j in range(rows)]
directions = [(0, 1), (1, 0), (-1, 0), (0, -1)]
board = []
for i in range(rows):
	board.append(input())

def valid_positions(i, j, i_other, j_other):
	if i >= rows or j >= columns or i < 0 or (j < 0):
		return False
	if board[i][j] != board[i_other][j_other]:
		return False
	return i != i_other or j != j_other

def search_differents_directions(row, column, i_other, j_other):
	for direction in directions:
		i_ = row + direction[0]
		j_ = column + direction[1]
		if valid_positions(i_, j_, i_other, j_other) and search_path(i_, j_, row, column):
			return True
	return False

def search_path(row, column, par_i, par_j):
	if visited[row][column]:
		return True
	visited[row][column] = True
	return search_differents_directions(row, column, par_i, par_j)

def fox_and_two_dots(row, column, row_i, column_j, flag):
	if row_i == row and column_j == column:
		if flag:
			print('Yes')
		else:
			print('No')
	else:
		flag_ = False
		if not visited[row_i][column_j]:
			if search_path(row_i, column_j, row_i, column_j):
				flag_ = True
				fox_and_two_dots(row, column, row, column, True)
		if not flag_:
			if column_j < column:
				fox_and_two_dots(row, column, row_i, column_j + 1, False)
			else:
				fox_and_two_dots(row, column, row_i + 1, 0, False)
fox_and_two_dots(rows - 1, columns - 1, 0, 0, False)


def dfs(curX, curY, fromX, fromY, color):
	visited[curX][curY] = True
	for wayIndex in range(len(dx)):
		newWayX = curX + dx[wayIndex]
		newWayY = curY + dy[wayIndex]
		if 0 <= newWayX < len(data) and 0 <= newWayY < len(data[0]):
			if not (fromX == newWayX and fromY == newWayY):
				if color == data[newWayX][newWayY]:
					if visited[newWayX][newWayY]:
						global result
						result = True
						return
					else:
						dfs(newWayX, newWayY, curX, curY, color)
import sys
sys.setrecursionlimit(10000)
(n, m) = tuple((int(x) for x in input().split()))
data = []
for i in range(n):
	data.append(list(input().strip()))
visited = [[False for i in range(m)] for j in range(n)]
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
result = False
for i in range(len(data)):
	for j in range(len(data[0])):
		if not visited[i][j]:
			dfs(i, j, -1, -1, data[i][j])
if result:
	print('Yes')
else:
	print('No')


def answer(m, n, A):
	for i in range(n):
		for j in range(m):
			d = {}
			s = [(i, j)]
			while s:
				cell = s.pop()
				r = cell[0]
				c = cell[1]
				if (r, c) in d:
					return 'Yes'
				d[r, c] = 1
				if r != 0 and (r - 1, c) not in d:
					if A[r - 1][c] == A[r][c]:
						s.append((r - 1, c))
				if c != 0 and (r, c - 1) not in d:
					if A[r][c - 1] == A[r][c]:
						s.append((r, c - 1))
				if r < n - 1 and (r + 1, c) not in d:
					if A[r + 1][c] == A[r][c]:
						s.append((r + 1, c))
				if c < m - 1 and (r, c + 1) not in d:
					if A[r][c + 1] == A[r][c]:
						s.append((r, c + 1))
	return 'No'
(n, m) = map(int, input().split())
l = []
for i in range(n):
	l.append(input())
print(answer(m, n, l))


from sys import setrecursionlimit
setrecursionlimit(10 ** 6)

def recursivefun(n=10000):
	if n > 0:
		recursivefun(n - 1)

def make_graph():
	dim = input().split(' ')
	graph = dict()
	line = []
	for n in range(int(dim[0])):
		line.append(input())
		for m in range(int(dim[1])):
			neighbour = []
			if m > 0 and line[n][m - 1] == line[n][m]:
				neighbour.append((n, m - 1, line[n][m - 1]))
				graph[n, m - 1, line[n][m - 1]].append((n, m, line[n][m]))
			if n > 0 and line[n - 1][m] == line[n][m]:
				neighbour.append((n - 1, m, line[n - 1][m]))
				graph[n - 1, m, line[n - 1][m]].append((n, m, line[n][m]))
			graph[n, m, line[n][m]] = neighbour
	return graph

def look_for_cycle(graph):
	reached = []

	def check_cycle(curr_v, past_v):
		isloop = False
		for next_v in graph[curr_v]:
			if next_v != past_v:
				if next_v in reached:
					isloop = True
					break
				elif next_v not in reached:
					reached.append(next_v)
					isloop = check_cycle(next_v, curr_v)
					if isloop == True:
						break
		return isloop
	for v in graph:
		if v not in reached:
			reached.append(v)
			isloop = check_cycle(v, None)
		if isloop == True:
			break
	return isloop

def bool_converter(state, pos, neg):
	if state == True:
		return str(pos)
	elif state == False:
		return str(neg)
	else:
		return None
a = bool_converter(look_for_cycle(make_graph()), 'Yes', 'No')
print(a)


import sys

def input():
	return sys.stdin.readline().strip()
(n, m) = map(int, input().split())
grid = []
for _ in range(n):
	s = input()
	value = list(s)
	grid.append(value)
n = len(grid)
m = len(grid[0])

def isvalid(x, y, vis):
	if x < 0 or x >= n or y < 0 or (y >= m):
		return False
	if vis[x][y] == 0:
		return True
	return False

def checkcycle(x, y, vis, n, m):
	dx = [0, 1, 0, -1]
	dy = [-1, 0, 1, 0]
	s = [[x, y]]
	pr = grid[x][y]
	parent = [[[-1, -1] for jj in range(m)] for ii in range(n)]
	while len(s) > 0:
		u = s[-1]
		if vis[u[0]][u[1]] == 0:
			vis[u[0]][u[1]] = 1
			check = 0
			checktwo = 0
			for i in range(0, 4):
				if isvalid(u[0] + dx[i], u[1] + dy[i], vis) and vis[u[0] + dx[i]][u[1] + dy[i]] == 0 and (pr == grid[u[0] + dx[i]][u[1] + dy[i]]):
					s.append([u[0] + dx[i], u[1] + dy[i]])
					check = 1
					parent[u[0] + dx[i]][u[1] + dy[i]] = [u[0], u[1]]
				if x == dx[i] + u[0] and y == dy[i] + u[1] and (parent[u[0]][u[1]] != [x, y]):
					checktwo = 1
			if check == 0 and checktwo == 1:
				return True
		else:
			s.pop()
	return False
f = 0
for i in range(n):
	for j in range(m):
		vis = [[0 for jj in range(m)] for ii in range(n)]
		ans = checkcycle(i, j, vis, n, m)
		if ans:
			f = 1
			break
	if f == 1:
		break
if f == 1:
	print('Yes')
else:
	print('No')


MAX = 50 * 50 + 7
parent = [0] * MAX
visited = {}
color = {}
(l, c) = list(map(int, input().split()))

def find(i):
	if i != parent[i]:
		parent[i] = find(parent[i])
	return parent[i]

def union(i, j):
	(pi, pj) = (find(i), find(j))
	if pi != pj:
		parent[pi] = pj

def get_val(no):
	return no[0] * c + no[1]

def ciclo(no, tipo):
	visited[no] = True
	for (i, j) in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
		v = (no[0] + i, no[1] + j)
		if v in visited:
			if not visited[v] and color[v] == tipo:
				if find(get_val(no)) == find(get_val(v)):
					return True
				union(get_val(v), get_val(no))
	return False
for i in range(l):
	linha = input()
	for j in range(c):
		pos = (i, j)
		color[pos] = linha[j]
		parent[get_val(pos)] = get_val(pos)
		visited[pos] = False
for i in range(l):
	for j in range(c):
		pos = (i, j)
		if ciclo(pos, color[pos]):
			print('Yes')
			exit()
print('No')


from sys import setrecursionlimit
import time
setrecursionlimit(10 ** 6)

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)
(m, n) = input().split(' ', 2)
lines = [list(input().upper().strip()) for i in range(int(m))]
g = UndirectedGraph()

def depth_first_search(g, v):
	reached = {}
	my_var = False

	def do_dfs(curr, prev):
		nonlocal my_var
		if my_var:
			return
		if curr in reached:
			my_var = True
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			if not succ == prev:
				do_dfs(succ, curr)
	do_dfs(v, v)
	return my_var

def create_graph(cur_pos, letter):
	if cur_pos[0] - 1 >= 0 and lines[cur_pos[0] - 1][cur_pos[1]] == letter:
		g.add_edge(str(cur_pos[0]) + ',' + str(cur_pos[1]), str(cur_pos[0] - 1) + ',' + str(cur_pos[1]))
	if cur_pos[1] - 1 >= 0 and lines[cur_pos[0]][cur_pos[1] - 1] == letter:
		g.add_edge(str(cur_pos[0]) + ',' + str(cur_pos[1]), str(cur_pos[0]) + ',' + str(cur_pos[1] - 1))
	if cur_pos[0] + 1 < int(m) and lines[cur_pos[0] + 1][cur_pos[1]] == letter:
		g.add_edge(str(cur_pos[0]) + ',' + str(cur_pos[1]), str(cur_pos[0] + 1) + ',' + str(cur_pos[1]))
	if cur_pos[1] + 1 < int(n) and lines[cur_pos[0]][cur_pos[1] + 1] == letter:
		g.add_edge(str(cur_pos[0]) + ',' + str(cur_pos[1]), str(cur_pos[0]) + ',' + str(cur_pos[1] + 1))
for i in range(int(m)):
	for j in range(int(n)):
		g.add_vertex(str(i) + ',' + str(j))
		create_graph((i, j), lines[i][j])
global found
found = False
for i in range(int(m)):
	if found:
		break
	for j in range(int(n)):
		if depth_first_search(g, str(i) + ',' + str(j)):
			found = True
			break
if found:
	print('Yes')
else:
	print('No')


import sys
sys.setrecursionlimit(10000000)
(n, m) = map(int, input().split())
a = (n + 2) * [(m + 2) * []]
a[0] = [(m + 2) * '*']
a[n + 1] = [(m + 2) * '*']
for i in range(1, n + 1, 1):
	a[i] = '*' + input() + '*'
discover = []

def findway(i, j, p):
	c = a[i][j]
	if i < n and p != 3:
		if a[i + 1][j] == c:
			if (i + 1, j) in discover:
				print('Yes')
				sys.exit()
			else:
				discover.append((i + 1, j))
			findway(i + 1, j, 1)
	if j < m and p != 4:
		if a[i][j + 1] == c:
			if (i, j + 1) in discover:
				print('Yes')
				sys.exit()
			else:
				discover.append((i, j + 1))
			findway(i, j + 1, 2)
	if i > 1 and p != 1:
		if a[i - 1][j] == c:
			if (i - 1, j) in discover:
				print('Yes')
				sys.exit()
			else:
				discover.append((i - 1, j))
			findway(i - 1, j, 3)
	if j > 1 and p != 2:
		if a[i][j - 1] == c:
			if (i, j - 1) in discover:
				print('Yes')
				sys.exit()
			else:
				discover.append((i, j - 1))
			findway(i, j - 1, 4)
for i in range(1, n, 1):
	for j in range(1, m, 1):
		if (i, j) not in discover:
			discover.append((i, j))
			findway(i, j, 0)
print('No')


(rows, cols) = map(int, input().split())
board = []
visited = {}

def has_cycle(start, prev, color):
	visited[start] = color
	(i, j) = (start[0], start[1])
	for (i, j) in [(i + 1, j), (i, j + 1), (i - 1, j), (i, j - 1)]:
		if 0 <= i < len(board) and 0 <= j < len(board[0]):
			if board[i][j] == color and (i, j) not in visited and ((i, j) != prev):
				c = has_cycle((i, j), start, color)
				if c:
					return True
			elif board[i][j] == color and (i, j) in visited and (visited[i, j] == color) and ((i, j) != prev):
				return True
	return False
for row in range(rows):
	board.append(input())

def cycle_exists():
	for i in range(0, len(board)):
		for j in range(0, len(board[i])):
			if (i, j) not in visited:
				c = has_cycle((i, j), (i, j), board[i][j])
				if c:
					return True
	return False
if cycle_exists():
	print('Yes')
else:
	print('No')


def dfs(dots, row, col):
	n = len(dots)
	m = len(dots[0])
	req = dots[row][col]
	req_row = row
	req_col = col
	stack = [(row, col, 1)]
	visited = set()
	while stack:
		curr = stack.pop()
		if curr[2] >= 4:
			if curr[0] == req_row and curr[1] == req_col:
				return True
		(row, col) = (curr[0], curr[1])
		if (row, col) not in visited:
			visited.add((row, col))
			if col + 1 < m:
				if dots[row][col + 1] == req:
					stack.append((row, col + 1, curr[2] + 1))
			if row + 1 < n:
				if dots[row + 1][col] == req:
					stack.append((row + 1, col, curr[2] + 1))
			if col - 1 >= 0:
				if dots[row][col - 1] == req:
					stack.append((row, col - 1, curr[2] + 1))
			if row - 1 >= 0:
				if dots[row - 1][col] == req:
					stack.append((row - 1, col, curr[2] + 1))
	return False

def main():
	(n, m) = map(int, input().split())
	dots = []
	for i in range(n):
		dots.append(input())
	for row in range(n):
		for col in range(m):
			if dfs(dots, row, col):
				print('Yes')
				return
	print('No')
main()


(n, m) = list(map(int, input().split()))
graph = [list(input()) for _ in range(n)]
exploredSet = set()

def findPath(startCoords):
	global exploredSet
	explore = list()
	explore.append((startCoords[0], startCoords[1]))
	while len(explore) > 0:
		curr = explore.pop()
		if curr in exploredSet:
			return True
		exploredSet.add(curr)
		a = curr[0]
		b = curr[1]
		aroundCoords = [(a + 1, b), (a - 1, b), (a, b + 1), (a, b - 1)]
		for coord in aroundCoords:
			if coord not in exploredSet and coord[0] < n and (coord[1] < m) and (coord[0] > -1) and (coord[1] > -1):
				if graph[coord[0]][coord[1]] == graph[startCoords[0]][startCoords[1]]:
					explore.append(coord)
	return False

def retval(n, m):
	for i in range(n):
		for j in range(m):
			if (i, j) not in exploredSet:
				if findPath((i, j)):
					print('Yes')
					return
	print('No')
retval(n, m)


(n, m) = map(int, input().split())
tabela = []
ciclo = False
finalizado = False

def degree(i, j):
	deg = 0
	for (x, y) in lstsos(i, j):
		deg += tabela[x][y] == tabela[i][j]
	return deg

def lstsos(i, j):
	array = [(i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1)]
	for (x, y) in array:
		if 0 <= x < n and 0 <= y < m:
			yield (x, y)
for x in range(n):
	tabela.append(list(input()))
while not finalizado:
	finalizado = True
	for x in range(n):
		for y in range(m):
			if tabela[x][y] != 'No' and degree(x, y) in (0, 1):
				tabela[x][y] = 'No'
				finalizado = False
for x in range(n):
	for y in range(m):
		if tabela[x][y] != 'No':
			ciclo = True
if ciclo:
	print('Yes')
else:
	print('No')


def main():
	(n, m) = map(int, input().split())
	a = []
	for _ in range(n):
		a.append(list(input()))
	c = [[False for _ in range(m)] for _ in range(n)]

	def cycle(x, y, px, py):
		if c[x][y]:
			return True
		c[x][y] = True
		dx = [-1, 1, 0, 0]
		dy = [0, 0, -1, 1]
		res = False
		for i in range(len(dx)):
			if 0 <= x + dx[i] <= n - 1 and 0 <= y + dy[i] <= m - 1:
				if (x + dx[i] != px or y + dy[i] != py) and a[x][y] == a[x + dx[i]][y + dy[i]]:
					res = res or cycle(x + dx[i], y + dy[i], x, y)
		return res
	ok = False
	for k in range(n):
		for l in range(m):
			if not c[k][l]:
				curr = cycle(k, l, -1, -1)
				if curr:
					ok = True
					break
	if ok:
		print('Yes')
	else:
		print('No')
main()


(n, m) = map(int, input().split())
colors = []
for i in range(n):
	input_colors = input()
	colors.append([])
	for color in input_colors:
		colors[i].append(color)
for i in range(n):
	for j in range(m):
		next_j = j + 1
		while next_j < m:
			if colors[i][j] != colors[i][next_j]:
				break
			next_i = i + 1
			while next_i < n:
				if colors[next_i][next_j] != colors[i][j]:
					break
				res = True
				for k in range(j, next_j + 1, 1):
					if colors[next_i][k] != colors[i][j]:
						res = False
				if res:
					print('Yes')
					exit()
				next_i += 1
			next_j += 1
print('No')


from collections import defaultdict
STACK = []
(n, m) = map(int, input().split())
lines = [input().strip() for _ in range(n)]
unique_colors = set()
G = {}
for i in range(n):
	for j in range(m):
		v = i * m + j
		G.setdefault(v, defaultdict(list))
		if j != 0:
			p = v - 1
			G.setdefault(p, defaultdict(list))
			if lines[i][j] == lines[i][j - 1]:
				color = lines[i][j]
				unique_colors.add(color)
				G[v][color].append(p)
				G[p][color].append(v)
		if i != 0:
			p = (i - 1) * m + j
			G.setdefault(p, defaultdict(list))
			if lines[i][j] == lines[i - 1][j]:
				color = lines[i][j]
				unique_colors.add(color)
				G[v][color].append(p)
				G[p][color].append(v)

def dfs(G, v, used, color):
	while STACK:
		v = STACK[-1]
		if used[v] == 0:
			used[v] = 1
			for c in G[v][color]:
				if c == parents[v]:
					continue
				if used[c] == 1:
					return True
				if used[c] == 0:
					parents[c] = v
					STACK.append(c)
		else:
			used[v] = 2
			STACK.pop()
	return False
for color in unique_colors:
	used = [0] * (n * m)
	parents = [None] * (n * m)
	for v in G.keys():
		if used[v] == 0:
			STACK = [v]
			if dfs(G, v, used, color):
				print('Yes')
				exit(0)
print('No')


(n, m) = map(int, input().split(' '))
a = []
for _ in range(n):
	a.append(input())
common_pairs_to_check = []
for i in range(n):
	for j in range(m):
		common_pairs_to_check.append((i, j))

def safe_check_matrix_element(a, i, j, x):
	n = len(a)
	m = len(a[0]) if n > 0 else None
	if not n or not m:
		return
	if 0 <= i < n and 0 <= j < m:
		return a[i][j] == x

def check_pairs(i, j, checked_pairs):
	should_check_pairs = [(i, j, (None, None))]
	while should_check_pairs:
		(i, j, prev) = should_check_pairs.pop()
		x1 = safe_check_matrix_element(a, i - 1, j, a[i][j]) and (i - 1, j) != prev
		x2 = safe_check_matrix_element(a, i + 1, j, a[i][j]) and (i + 1, j) != prev
		x3 = safe_check_matrix_element(a, i, j - 1, a[i][j]) and (i, j - 1) != prev
		x4 = safe_check_matrix_element(a, i, j + 1, a[i][j]) and (i, j + 1) != prev
		if (i, j) in checked_pairs:
			print('Yes')
			exit()
		checked_pairs.append((i, j))
		if x1 is True:
			should_check_pairs.append((i - 1, j, (i, j)))
		if x2 is True:
			should_check_pairs.append((i + 1, j, (i, j)))
		if x3 is True:
			should_check_pairs.append((i, j - 1, (i, j)))
		if x4 is True:
			should_check_pairs.append((i, j + 1, (i, j)))
while common_pairs_to_check:
	checked_pairs = []
	(i, j) = common_pairs_to_check.pop()
	check_pairs(i, j, checked_pairs)
	common_pairs_to_check = list(set(common_pairs_to_check) - set(checked_pairs))
print('No')


def dfs(node, p):
	children = []
	if node[0] < n - 1:
		children.append((node[0] + 1, node[1]))
	if node[1] > 0:
		children.append((node[0], node[1] - 1))
	if node[0] > 0:
		children.append((node[0] - 1, node[1]))
	if node[1] < m - 1:
		children.append((node[0], node[1] + 1))
	for child in children:
		if len(p) > 1 and child == p[-2]:
			continue
		if letters[child[0]][child[1]] == letters[node[0]][node[1]] and child in not_visited:
			if child in p:
				return True
			p.append(child)
			res = dfs(child, p)
			if res:
				return True
			p.pop()
	not_visited.remove(node)
	return False
(n, m) = list(map(int, input().split()))
letters = [input() for _ in range(n)]
not_visited = set()
for i in range(n):
	for j in range(m):
		not_visited.add((i, j))
while len(not_visited) > 0:
	path = []
	node = not_visited.pop()
	not_visited.add(node)
	path.append(node)
	found = dfs(node, path)
	if found:
		print('Yes')
		exit()
print('No')


import sys
sys.setrecursionlimit(10 ** 5)
from collections import deque
(n, m) = map(int, input().split(' '))
cor = []
for x in range(n):
	cor.append([0] * m)
grid = []
g = []
for a in range(n):
	g.append([])
	for b in range(m):
		g[a].append([])
for x in range(n):
	linha = input()
	linha = list(linha)
	grid.append(linha)
for i in range(n):
	for j in range(m):
		if i and grid[i][j] == grid[i - 1][j]:
			g[i][j].append((i - 1, j))
		if j and grid[i][j] == grid[i][j - 1]:
			g[i][j].append((i, j - 1))
		if i < n - 1 and grid[i][j] == grid[i + 1][j]:
			g[i][j].append((i + 1, j))
		if j < m - 1 and grid[i][j] == grid[i][j + 1]:
			g[i][j].append((i, j + 1))

def achaciclo(i, j, pre):
	cor[i][j] = 1
	for v in g[i][j]:
		if v == pre:
			continue
		if cor[v[0]][v[1]] == 0:
			achaciclo(v[0], v[1], (i, j))
		if cor[v[0]][v[1]] in [1, 3]:
			cor[i][j] = 3
	if cor[i][j] == 1:
		cor[i][j] = 2
ciclo = 'No'
for i in range(n):
	for j in range(m):
		if cor[i][j] == 0:
			achaciclo(i, j, (-1, -1))
			if cor[i][j] == 3:
				ciclo = 'Yes'
print(ciclo)


import sys
sys.setrecursionlimit(10000)
(rows, columns) = map(int, input().split())
visited = [[False for i in range(columns)] for j in range(rows)]
directions = [(0, 1), (1, 0), (-1, 0), (0, -1)]
matriz = []
for i in range(rows):
	matriz.append(input())

def check(i, j, i2, j2):
	if i >= rows or j >= columns or i < 0 or (j < 0):
		return False
	if matriz[i][j] != matriz[i2][j2]:
		return False
	return i != i2 or j != j2

def dfs(row, column, par_i, par_j):
	if visited[row][column]:
		return True
	visited[row][column] = True
	for (inc_i, inc_j) in directions:
		new_i = row + inc_i
		new_j = column + inc_j
		if check(new_i, new_j, par_i, par_j) and dfs(new_i, new_j, row, column):
			return True
	return False

def fox_and_two_dots(row, column, row_i, column_j, flag):
	if row_i == row and column_j == column:
		if flag:
			print('Yes')
			return
		else:
			print('No')
			return
	else:
		flag_ = False
		if not visited[row_i][column_j]:
			if dfs(row_i, column_j, row_i, column_j):
				flag_ = True
				fox_and_two_dots(row, column, row, column, True)
		if not flag_:
			if column_j < column:
				fox_and_two_dots(row, column, row_i, column_j + 1, False)
			else:
				fox_and_two_dots(row, column, row_i + 1, 0, False)
fox_and_two_dots(rows - 1, columns - 1, 0, 0, False)


(n, m) = list(map(int, input().split()))
l = [[] for i in range(n)]
for i in range(n):
	x = input()
	for j in x:
		l[i].append(ord(j))
for i in range(n):
	for j in range(m):
		x = j + 1
		while x < m and l[i][j] == l[i][x]:
			y = i + 1
			while y < n and l[y][x] == l[i][j]:
				r = 1
				for k in range(j, x + 1, 1):
					if l[y][k] != l[i][j]:
						r = 0
				if r == 1:
					print('Yes')
					exit()
				y += 1
			x += 1
print('No')


import sys
sys.setrecursionlimit(10000)
dir = [[0, 1], [0, -1], [-1, 0], [1, 0]]
matrix = []
visited = []
founded = False

def dfs(x, y, n, m, color, ox, oy):
	visited[x][y] = 1
	for elem in dir:
		nx = x + elem[0]
		ny = y + elem[1]
		if nx >= 0 and nx < n and (ny >= 0) and (ny < m) and (matrix[nx][ny] == color):
			if visited[nx][ny] == 0:
				dfs(nx, ny, n, m, color, x, y)
			elif nx != ox and ny != oy:
				global founded
				founded = True
				return
(n, m) = list(map(int, input().split()))
for i in range(n):
	matrix.append(input())
	visited.append([0] * m)
for j in range(n):
	for k in range(m):
		if visited[j][k] == 0:
			dfs(j, k, n, m, matrix[j][k], -1, -1)
			if founded:
				print('Yes')
				quit()
print('No')


import sys
sys.setrecursionlimit(5000)
graph = {}

def dfs(v, reached):

	def do_dfs(curr, prev):
		reached.add(curr)
		for succ in graph[curr]:
			if succ == prev:
				continue
			if succ in reached:
				return True
			if do_dfs(succ, curr):
				return True
		return False
	return do_dfs(v, v)
(n, m) = map(int, input().split())
vertices = {}
for i in range(n):
	temp = input()
	for j in range(m):
		vertices[i, j] = temp[j]
		if (i, j) not in graph:
			graph[i, j] = set()
		if j > 0 and temp[j] == vertices[i, j - 1]:
			graph[i, j].add((i, j - 1))
			graph[i, j - 1].add((i, j))
		if i > 0 and temp[j] == vertices[i - 1, j]:
			graph[i, j].add((i - 1, j))
			graph[i - 1, j].add((i, j))
got_cycle = False
reached = set()
for v in graph.keys():
	if v not in reached:
		got_cycle = dfs(v, reached)
	if got_cycle:
		break
print('Yes' if got_cycle else 'No')


from sys import setrecursionlimit
from collections import deque
setrecursionlimit(10 ** 5)
(n, m) = [int(x) for x in input().split(' ')]
matrix = []
for i in range(n):
	matrix.append([x for x in input()])
color = [[0 for j in range(m)] for i in range(n)]
g = {i: {j: [] for j in range(m)} for i in range(n)}
for i in range(n):
	for j in range(m):
		if i and matrix[i][j] == matrix[i - 1][j]:
			g[i][j].append((i - 1, j))
		if j and matrix[i][j] == matrix[i][j - 1]:
			g[i][j].append((i, j - 1))
		if i < n - 1 and matrix[i][j] == matrix[i + 1][j]:
			g[i][j].append((i + 1, j))
		if j < m - 1 and matrix[i][j] == matrix[i][j + 1]:
			g[i][j].append((i, j + 1))

def getCycle(i, j, pre):
	color[i][j] = 1
	for v in g[i][j]:
		if v != pre:
			if color[v[0]][v[1]] == 0:
				getCycle(v[0], v[1], (i, j))
			if color[v[0]][v[1]] in [1, 3]:
				color[i][j] = 3
	if color[i][j] == 1:
		color[i][j] = 2
cycle = 'No'
for i in range(n):
	for j in range(m):
		if color[i][j] == 0:
			getCycle(i, j, (-1, -1))
			if color[i][j] == 3:
				cycle = 'Yes'
print(cycle)


(n, m) = map(int, input().split())
arr = []
for _ in range(n):
	arr.append(input())
vis = [[-1 for i in range(m)] for j in range(n)]
val = [[0 for i in range(m)] for j in range(n)]

def valid(i, j):
	if i >= 0 and i <= n - 1 and (j >= 0) and (j <= m - 1):
		return True
	else:
		return False

def dfs(i, j, l, i1, j1):
	boo_l = False
	vis[i][j] = 1
	poss = [(i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)]
	for x in poss:
		if valid(x[0], x[1]):
			if arr[i][j] == arr[x[0]][x[1]] and vis[x[0]][x[1]] == 1 and (l >= 4) and ((x[0], x[1]) != (i1, j1)):
				val[i][j] = 1
				boo_l = True
				break
	if not boo_l:
		for x in poss:
			if valid(x[0], x[1]):
				if arr[i][j] == arr[x[0]][x[1]] and vis[x[0]][x[1]] != 1 and ((x[0], x[1]) != (i1, j1)):
					dfs(x[0], x[1], l + 1, i, j)
for i in range(n):
	for j in range(m):
		if vis[i][j] != 1:
			dfs(i, j, 1, -1, -1)
final = 0
for i in range(n):
	for j in range(m):
		if val[i][j] == 1:
			final += 1
if final > 0:
	print('Yes')
else:
	print('No')


class node:

	def __init__(self, data, adj=[], searched=0, parent=None):
		self._data = data
		self._adj = adj
		self._searched = searched
		self._parent = parent
(n, m) = [int(i) for i in input().split()]
global stack
nodes = list()

def judge(node):
	global stack
	for item in node._adj:
		if item._data == node._data and item._searched == 1 and (node._parent != item):
			return True
		elif item._searched == 0:
			item._parent = node
			stack.append(item)
	return False

def dfs(node):
	global stack
	stack = list()
	judge(node)
	node._searched = 1
	while len(stack) > 0:
		tem = stack.pop()
		if tem._data == node._data:
			if judge(tem):
				return True
			tem._searched = 1
	return False
for i in range(n):
	tem = input()
	temp = list()
	for j in range(m):
		temp.append(node(tem[j], []))
	nodes.append(temp)
for i in range(n):
	for j in range(m):
		if i - 1 >= 0:
			nodes[i - 1][j]._adj.append(nodes[i][j])
		if i + 1 < n:
			nodes[i + 1][j]._adj.append(nodes[i][j])
		if j - 1 >= 0:
			nodes[i][j - 1]._adj.append(nodes[i][j])
		if j + 1 < m:
			nodes[i][j + 1]._adj.append(nodes[i][j])
done = 0
for i in range(n):
	for j in range(m):
		if nodes[i][j]._searched == 0:
			if dfs(nodes[i][j]):
				print('Yes')
				done = 1
				exit()
if done == 0:
	print('No')


(n, m) = map(int, input().split())
ma = [input() for _ in range(n)]
gs = {}
g = {}
for i in range(n):
	for j in range(m):
		if ma[i][j] not in gs:
			gs[ma[i][j]] = [(i, j)]
		else:
			gs[ma[i][j]].append((i, j))
for i in gs:
	g = {}
	for j in gs[i]:
		g[j] = []
		if (j[0] - 1, j[1]) in gs[i]:
			g[j].append((j[0] - 1, j[1]))
		if (j[0] + 1, j[1]) in gs[i]:
			g[j].append((j[0] + 1, j[1]))
		if (j[0], j[1] - 1) in gs[i]:
			g[j].append((j[0], j[1] - 1))
		if (j[0], j[1] + 1) in gs[i]:
			g[j].append((j[0], j[1] + 1))
	visited = []
	stack = []
	flag = 0
	while flag == 0:
		if len(g) == len(visited):
			break
		for jj in g:
			if jj not in visited:
				stack.append(jj)
				break
		while len(stack) != 0:
			e = stack.pop()
			if e in visited:
				flag = 1
				break
			visited.append(e)
			for kk in g[e]:
				if kk not in visited:
					stack.append(kk)
	if flag:
		print('Yes')
		exit()
print('No')


from collections import deque, namedtuple
(n, m) = map(int, input().split())
c = [input() for _ in ' ' * n]
p = namedtuple('p', ['x', 'y', 'parent'])
visited = [[False for _ in ' ' * m] for _ in ' ' * n]
y = [-1, 0, 1, 0]
x = [0, -1, 0, 1]

def judge(x, y):
	if x >= 0 and x < n and (y >= 0) and (y < m):
		return 1
	else:
		return 0

def isCyclicUtil(i, j):
	dq = deque()
	dq.append(p(i, j, (-1, -1)))
	while len(dq):
		q = dq.pop()
		if visited[q.x][q.y]:
			return True
		visited[q.x][q.y] = True
		for k in range(4):
			u = q.x + y[k]
			v = q.y + x[k]
			if not (judge(u, v) and c[u][v] == c[i][j]):
				continue
			if not (u == q.parent[0] and v == q.parent[1]):
				dq.append(p(u, v, (q.x, q.y)))

def iscycle(n, m):
	for i in range(n):
		for j in range(m):
			if visited[i][j] == False:
				if isCyclicUtil(i, j) == True:
					return True
	return False
print('Yes' if iscycle(n, m) else 'No')


__author__ = 'Utena'
s = [[0 for i in range(53)] for j in range(53)]
f = [[0 for i in range(53)] for j in range(53)]
get = []
conn = [[[] for i in range(53)] for j in range(53)]
(n, k) = map(int, input().split())
flag = 'No'
for i in range(1, n + 1):
	m = list(input())
	for j in range(1, k + 1):
		s[i][j] = m[j - 1]
for i in range(1, n + 1):
	for j in range(1, k + 1):
		if s[i][j] == s[i + 1][j]:
			conn[i][j].append((i + 1, j))
			conn[i + 1][j].append((i, j))
		if s[i][j] == s[i][j + 1]:
			conn[i][j].append((i, j + 1))
			conn[i][j + 1].append((i, j))

def dfs(i, j):
	global flag
	for a in conn[j[0]][j[1]]:
		if a != i:
			if f[a[0]][a[1]] == 1:
				flag = 'Yes'
				return 1
			else:
				f[a[0]][a[1]] = 1
				dfs(j, a)
	return 0
for i in range(1, n + 1):
	for j in range(1, k + 1):
		if f[i][j] != 1:
			f[i][j] = 1
			dfs((0, 0), (i, j))
print(flag)


class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		self.add_vertex(source)
		self.add_vertex(destination)
		self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

class UndirectedGraph(Graph):

	def add_edge(self, a, b):
		super().add_edge(a, b)
		super().add_edge(b, a)

def depth_first_search(g, v):
	reached = {}

	def do_dfs(curr, prev):
		if curr in reached:
			return
		reached[curr] = prev
		for succ in g.neighbours(curr):
			do_dfs(succ, curr)
	do_dfs(v, v)
	return reached
from sys import setrecursionlimit
setrecursionlimit(1000000)

def cycleExists(g):
	global cycleDetected
	global vertexMarked
	for v in g.vertices():
		vertexMarked[v] = False
	for v in g.vertices():
		if not vertexMarked[v]:
			dfs_modified(g, v, v)
		if cycleDetected:
			break
	return

def dfs_modified(g, curr, prev):
	global cycleDetected
	global vertexMarked
	if cycleDetected:
		return
	vertexMarked[curr] = True
	for succ in g.neighbours(curr):
		if vertexMarked[succ] and succ != prev:
			cycleDetected = True
			return
		if not vertexMarked[succ]:
			dfs_modified(g, succ, curr)

def MatrixGenerate():
	line_1 = input()
	m = int(line_1.split()[0])
	n = int(line_1.split()[1])
	matrix = [[0 for i in range(n)] for j in range(m)]
	for a in range(m):
		line_2 = input()
		for b in range(n):
			matrix[a][b] = line_2[b]
	return [matrix, m, n]

def GraphStore(matrix, m, n):
	g = Graph()
	for a in range(m):
		for b in range(n):
			g.add_vertex(a * n + b)
	for a in range(m):
		for b in range(n):
			if not a < 1:
				if matrix[a][b] == matrix[a - 1][b]:
					g.add_edge(a * n + b, (a - 1) * n + b)
			if not a > m - 2:
				if matrix[a][b] == matrix[a + 1][b]:
					g.add_edge(a * n + b, (a + 1) * n + b)
			if not b < 1:
				if matrix[a][b] == matrix[a][b - 1]:
					g.add_edge(a * n + b, a * n + b - 1)
			if not b > n - 2:
				if matrix[a][b] == matrix[a][b + 1]:
					g.add_edge(a * n + b, a * n + b + 1)
	return g
cycleDetected = False
vertexMarked = {}
matrix = MatrixGenerate()
g = GraphStore(matrix[0], matrix[1], matrix[2])
cycleExists(g)
if cycleDetected:
	print('Yes')
else:
	print('No')


def check(f1, f2):
	if f1 >= 0 and f1 <= n - 1 and (f2 >= 0) and (f2 <= m - 1):
		return True
	return False
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

def dfs(x, y, c, pa, py):
	vis[x][y] = c
	for i in range(4):
		a = dx[i] + x
		b = dy[i] + y
		if check(a, b) == True:
			if not vis[a][b] and g[a][b] == c:
				dfs(a, b, c, x, y)
			elif vis[a][b] == c and a != pa and (b != py):
				print('Yes')
				exit(0)
(n, m) = map(int, input().split())
g = []
for i in range(n):
	g.append(list(input()))
vis = [[0] * m for i in range(n)]
for i in range(n):
	for j in range(m):
		if not vis[i][j]:
			dfs(i, j, g[i][j], -1, -1)
print('No')


from queue import deque

class Graph:

	def __init__(self):
		self._alist = {}

	def add_vertex(self, vertex):
		if vertex not in self._alist:
			self._alist[vertex] = set()

	def add_edge(self, source, destination):
		if source != destination:
			self.add_vertex(source)
			self.add_vertex(destination)
			self._alist[source].add(destination)

	def is_edge(self, source, destination):
		return self.is_vertex(source) and destination in self._alist[source]

	def is_vertex(self, vertex):
		return vertex in self._alist

	def neighbours(self, vertex):
		return self._alist[vertex]

	def vertices(self):
		return self._alist.keys()

def isCyclic(g):
	todo = deque()
	marker = {vertex: -1 for vertex in g.vertices()}
	for v in g.vertices():
		if marker[v] != -1:
			continue
		marker[v] = 0
		todo.append(v)
		while todo:
			curr = todo.popleft()
			for succ in g.neighbours(curr):
				if marker[succ] == -1:
					marker[succ] = marker[curr] + 1
					todo.append(succ)
				elif marker[succ] >= marker[curr]:
					return True
	return False

def createBoardGraph(boardArray):
	boardGraph = Graph()
	for y in range(len(boardArray)):
		for x in range(len(boardArray[y])):
			boardGraph.add_vertex((x, y))
			if x != 0 and boardArray[y][x] == boardArray[y][x - 1]:
				boardGraph.add_edge((x, y), (x - 1, y))
			if x < boardSize[1] - 1 and boardArray[y][x] == boardArray[y][x + 1]:
				boardGraph.add_edge((x, y), (x + 1, y))
			if y != 0 and boardArray[y][x] == boardArray[y - 1][x]:
				boardGraph.add_edge((x, y), (x, y - 1))
			if y < boardSize[0] - 1 and boardArray[y][x] == boardArray[y + 1][x]:
				boardGraph.add_edge((x, y), (x, y + 1))
	return boardGraph
boardSize = input().split()
boardSize[0] = int(boardSize[0])
boardSize[1] = int(boardSize[1])
boardArray = list()
for row in range(boardSize[0]):
	boardArray.append(list(input()))
boardGraph = createBoardGraph(boardArray)
if isCyclic(boardGraph):
	print('Yes')
else:
	print('No')


import sys
from collections import deque
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 6)

def isValid(i, j, n, m, g, ch):
	if i == n or j == m or i < 0 or (j < 0):
		return False
	else:
		if g[i][j] != ch:
			return False
		return True
(n, m) = list(map(int, input().split()))
g = []
for i in range(n):
	g.append(list(input()))
s = set()
for i in range(n):
	for j in range(m):
		s.add(g[i][j])
visited = []
parent = []
for i in range(n):
	visited.append([0] * m)
	parent.append([0] * m)
f = 0
for ch in s:
	for i in range(n):
		for j in range(m):
			if g[i][j] == ch and visited[i][j] == 0:
				q = deque()
				q.append([i, j])
				visited[i][j] = 1
				while q:
					(x, y) = q.popleft()
					if isValid(x + 1, y, n, m, g, ch):
						if visited[x + 1][y] == 0:
							parent[x + 1][y] = [x, y]
							q.append([x + 1, y])
							visited[x + 1][y] = 1
						elif parent[x][y] != [x + 1, y]:
							f = 1
							break
					if isValid(x - 1, y, n, m, g, ch):
						if visited[x - 1][y] == 0:
							parent[x - 1][y] = [x, y]
							q.append([x - 1, y])
							visited[x - 1][y] = 1
						elif parent[x][y] != [x - 1, y]:
							f = 1
							break
					if isValid(x, y + 1, n, m, g, ch):
						if visited[x][y + 1] == 0:
							parent[x][y + 1] = [x, y]
							q.append([x, y + 1])
							visited[x][y + 1] = 1
						elif parent[x][y] != [x, y + 1]:
							f = 1
							break
					if isValid(x, y - 1, n, m, g, ch):
						if visited[x][y - 1] == 0:
							parent[x][y - 1] = [x, y]
							q.append([x, y - 1])
							visited[x][y - 1] = 1
						elif parent[x][y] != [x, y - 1]:
							f = 1
							break
				if f:
					break
		if f:
			break
if f:
	print('Yes')
else:
	print('No')


(n, m) = map(int, input().split(' '))
s = [list(input() + '.') for i in range(n)]
s += [list('.' * (m + 1))]

def solve():
	flag = False
	for i in range(n):
		for j in range(m):
			if s[i][j] != '.':
				d = 0
				d += s[i + 1][j] == s[i][j]
				d += s[i - 1][j] == s[i][j]
				d += s[i][j + 1] == s[i][j]
				d += s[i][j - 1] == s[i][j]
				if d < 2:
					s[i][j] = '.'
					flag = True
	return flag
for _ in range(500):
	solve()
for i in range(n):
	for j in range(m):
		if s[i][j] != '.':
			print('Yes')
			exit()
print('No')


import sys
input = lambda : sys.stdin.readline().strip('\r\n')
(n, m) = map(int, input().split())
g = []
for _ in range(n):
	g.append(list(input()))
vis = [[0] * m for _ in range(n)]

def dfs(x, y, c, pa, py):
	vis[x][y] = c
	d = [[-1, 0], [0, -1], [1, 0], [0, 1]]
	for (i, j) in d:
		a = x + i
		b = y + j
		if 0 <= a < n and 0 <= b < m:
			if not vis[a][b] and g[a][b] == c:
				dfs(a, b, c, x, y)
			elif vis[a][b] == c and a != pa and (b != py):
				print('Yes')
				exit()
c = 1
for i in range(n):
	for j in range(m):
		if not vis[i][j]:
			dfs(i, j, g[i][j], -1, -1)
			c += 1
print('No')


from sys import stdin, stdout

def findDFS(ls, i, j, start, end, visited, st, n, m, count):
	count += 1
	if count >= 4 and (abs(start - i) == 1 and end == j or (abs(end - j) == 1 and start == i)):
		return True
	if i > 0:
		if ls[i - 1][j] == st and (not visited[i - 1][j]):
			visited[i - 1][j] = True
			if findDFS(ls, i - 1, j, start, end, visited, st, n, m, count):
				return True
			else:
				visited[i - 1][j] = False
	if j > 0:
		if ls[i][j - 1] == st and (not visited[i][j - 1]):
			visited[i][j - 1] = True
			if findDFS(ls, i, j - 1, start, end, visited, st, n, m, count):
				return True
			else:
				visited[i][j - 1] = False
	if i < n:
		if ls[i + 1][j] == st and (not visited[i + 1][j]):
			visited[i + 1][j] = True
			if findDFS(ls, i + 1, j, start, end, visited, st, n, m, count):
				return True
			else:
				visited[i + 1][j] = False
	if j < m:
		if ls[i][j + 1] == st and (not visited[i][j + 1]):
			visited[i][j + 1] = True
			if findDFS(ls, i, j + 1, start, end, visited, st, n, m, count):
				return True
			else:
				visited[i][j + 1] = False
	return False
(n, m) = map(int, stdin.readline().split())
ls = [[0 for i in range(m)] for j in range(n)]
for i in range(n):
	a = stdin.readline()
	for j in range(m):
		ls[i][j] = a[j]
c = 0
for i in range(n):
	for j in range(m):
		visited = [[False for i in range(m)] for j in range(n)]
		st = ls[i][j]
		visited[i][j] = True
		if findDFS(ls, i, j, i, j, visited, st, n - 1, m - 1, 0):
			c = 1
			break
	if c == 1:
		break
if c == 1:
	print('Yes')
else:
	print('No')


(n, m) = [int(x) for x in input().split()]
matrix = []
colors = set()
for i in range(n):
	line = list(input().strip())
	colors.update(set(line))
	matrix.append(line)
colors = list(colors)
marked = [x.lower() for x in colors]
directions = [(1, 0), (0, 1), (-1, 0), (0, -1)]
stack = []
actual = (matrix[0][0], matrix[0][0].lower())
before = (n + 1, m + 1)
for k in range(n):
	for l in range(m):
		if matrix[k][l] in colors:
			actual = (matrix[k][l], matrix[k][l].lower())
			stack.append([(k, l), before])
			matrix[k][l] = actual[1]
		while not stack == []:
			((i, j), parent) = stack.pop()
			if matrix[i][j] == actual[1]:
				for d in directions:
					idy = i + d[0]
					idx = j + d[1]
					if idy >= 0 and idy < n and (idx >= 0) and (idx < m):
						if matrix[idy][idx] == actual[1] and (idy != parent[0] or idx != parent[1]):
							print('Yes')
							quit()
						if matrix[idy][idx] == actual[0]:
							matrix[idy][idx] = actual[1]
							stack.append([(idy, idx), (i, j)])
print('No')


from typing import List, Tuple

class Tile:

	def __init__(self, color: str, was_here: bool=False):
		self.color = color
		self.was_here = was_here

	def __str__(self) -> str:
		return f"|{(self.color if not self.was_here else '.')}|"

	def __repr__(self) -> str:
		return str(self)

def print_field(field: List[List[Tile]]):
	for row in field:
		for tile in row:
			print(tile, end='')
		print()

def get_neighbours(i: int, j: int, n: int, m: int):
	if j + 1 < m:
		yield (i, j + 1)
	if i + 1 < n:
		yield (i + 1, j)
	if j > 0:
		yield (i, j - 1)
	if i > 0:
		yield (i - 1, j)

def look_for_a_loop(field: List[List[Tile]], i: int, j: int, from_: Tuple[int, int]) -> bool:
	(n, m) = (len(field), len(field[0]))
	current = field[i][j]
	current.was_here = True
	from_tile = field[from_[0]][from_[1]]
	result = False
	for (ni, nj) in get_neighbours(i, j, n, m):
		neighbour = field[ni][nj]
		if neighbour == from_tile or neighbour.color != current.color:
			continue
		if neighbour.was_here:
			return True
		if look_for_a_loop(field, ni, nj, (i, j)):
			return True

def solve(n: int, m: int, field: List[List[Tile]]) -> str:
	for i in range(n):
		for j in range(m):
			tile = field[i][j]
			if tile.was_here:
				continue
			if look_for_a_loop(field, i, j, (i, j)):
				return 'Yes'
	return 'No'

def main():
	(n, m) = map(int, input().split())
	field = []
	for _ in range(n):
		row = [Tile(color) for color in input()]
		field.append(row)
	print(solve(n, m, field))
main()


from typing import List

def find_adj_moves(x, y):
	return [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]

def solve(n: int, m: int, board: List[str]) -> str:
	safe_board = ['.' * (m + 2)] + ['.' + r + '.' for r in board] + ['.' * (m + 2)]
	visited = [[0] * (m + 2) for _ in range(n + 2)]
	for i in range(1, n + 1):
		for j in range(1, m + 1):
			if not visited[i][j]:
				stack = [(i, j)]
				while stack:
					(x, y) = stack.pop()
					visited[x][y] += 1
					if visited[x][y] == 2:
						return 'Yes'
					for (xo, yo) in find_adj_moves(x, y):
						if safe_board[xo][yo] == safe_board[x][y] and (not visited[xo][yo]) and (1 <= xo <= n) and (1 <= yo <= m):
							stack.append((xo, yo))
	return 'No'
(n, m) = map(int, input().split())
board = [input() for i in range(n)]
print(solve(n, m, board))


import sys
sys.setrecursionlimit(50 * 50)
(n, m) = map(int, input().split())
to_visit = set(((x, y) for x in range(n) for y in range(m)))

def bfs(game, node, root, viscolor=None):
	if viscolor is None:
		viscolor = set()
	char = game[node[0]][node[1]]
	viscolor.add(node)
	to_visit.discard(node)
	neighbors = [(1, 0), (-1, 0), (0, 1), (0, -1)]
	for neigh in neighbors:
		(x, y) = (neigh[0] + node[0], neigh[1] + node[1])
		if 0 <= x < len(game) and 0 <= y < len(game[0]) and ((x, y) != root):
			nxt_char = game[x][y]
			if nxt_char == char:
				if (x, y) not in viscolor:
					if bfs(game, (x, y), node, viscolor):
						return True
				else:
					return True
	return False

def main():
	game = []
	for _ in range(n):
		game.append(input())
	while to_visit:
		node = to_visit.pop()
		if bfs(game, node, node):
			print('Yes')
			return
	print('No')
main()


[n, m] = [int(x) for x in input().split()]
vis = [[False for i in range(55)] for i in range(0, 55)]

def isSafe(x, y) -> bool:
	return x >= 0 and x < n and (y >= 0) and (y < m)
s = []
f = False
for i in range(0, n):
	_in = input()
	s.append(_in)
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]

def dfs(x, y, px, py):
	vis[x][y] = True
	for i in range(0, 4):
		(nx, ny) = (x + dx[i], y + dy[i])
		if isSafe(nx, ny) and s[x][y] == s[nx][ny] and ((nx == px and ny == py) == False):
			if vis[nx][ny]:
				return True
			elif dfs(nx, ny, x, y):
				return True
	return False
for i in range(0, n):
	for j in range(0, m):
		if vis[i][j] == False:
			if dfs(i, j, -1, -1):
				f = True
				break
if f:
	print('Yes')
else:
	print('No')