问题描述
给定一个大小为 N 的小写字符串 A[] 的数组,确定这些字符串是否可以链接在一起形成一个圆圈。一种 如果 X 的最后一个字符与第一个字符相同,则字符串 X 可以与另一个字符串 Y 链接在一起 Y 的字符。如果数组中的每个字符串都可以链接起来,就会形成一个圆圈。
例如对于数组 arr[] = {"for","geek","rig","kaf"}
,答案将是 Yes,因为给定的字符串可以链接为 "for","geek" and "kaf"
示例 1:
Input:
N = 3
A[] = { "abc","bcd","cdf" }
Output:
0
Explaination:
These strings can't form a circle
because no string has 'd'at the starting index.
示例 2:
Input:
N = 4
A[] = { "ab","bc","cd","da" }
Output:
1
Explaination:
These strings can form a circle
of strings.
我的工作正确代码:
class Solution:
def isCircle(self,N,A):
visited = set()
item1 = A[0]
curr = item1[-1]
visited.add(item1)
for _ in range(len(A)):
for item in A:
if item not in visited and item[0] == curr:
visited.add(item)
curr = item[-1]
# return visited
if len(visited) == len(A):
return 1
else:
return 0
当前时间复杂度:
O(n2)
预期时间复杂度:
O(n)
如何提高效率并在更短的时间内完成相同的工作?
解决方法
构建图表:
- 每个字母的节点。
- 每个单词的有向边(u,v),u是单词的第一个字母,v是最后一个字母。
就是这样,这就够了。此图中的路径代表一串单词。 (如果需要重构的话,可以把边上的词保存起来,不影响复杂度。
该图的构建成本为 O(n),因为它有 26 个顶点和 n 个边。
检查图是否强连通
观察:如果图不是强连通的,则其所有边都不可能存在循环。该检查可以通过 Tarjan's 算法在 O(V + E) 时间内完成。由于边的数量对应于我们拥有的单词数量,因此我们的目的是 O(n)。 (或者很可能更快,因为如果我们采用 a-z 字母表,我们只有 26 个顶点)
找到欧拉循环:
观察:正如我们所提到的,图中的路径是一个词链。我们正在寻找一个可以遍历所有边的循环。这是一个经过充分研究的寻找欧拉循环的问题。
由于图是强连通的,所以只要图中的所有度数都是偶数,欧拉循环就存在。检查这些边可以在与顶点数量成线性关系的时间内完成,因此它对复杂性没有任何影响,因为它们只有 26 个。如果我们去找到循环链,事情会有点困难 - 但是它可以在 O(E) 中通过例如 Hierholzer's 算法完成。
由于问题只是一个是/否决定,因此不需要找到循环。
注意:由于有关于以相同字母开头和结尾的单词的注释:这些不会改变结果,因为它们中的每一个都向图中添加了一个单节点循环,将其度增加 2。
,实现Shamis的想法! 在 0.09 秒内运行
from collections import defaultdict
class Solution:
def __init__(self):
self.vertList = defaultdict(list)
def addEdges(self,u,v):
self.vertList[u].append(v)
# <- function to generate reversed graph ->
def reverseTheGraph(self):
g1 = Solution()
for v in self.vertList:
for nbr in self.vertList[v]:
g1.addEdges(nbr,v) # reverse graph means the edges are reversed
return g1
# <- function for normal DFS traversal ->
def DFS(self,v1,visited2,ans):
visited2.add(v1)
for nbr in self.vertList[v1]:
if nbr not in visited2:
self.DFS(nbr,ans)
# <- function for finished time DFS traversal ->
def finishedTimeDFS(self,givenVertex,visited1,stack):
visited1.add(givenVertex)
for nbr in self.vertList[givenVertex]:
if nbr not in visited1:
self.finishedTimeDFS(nbr,stack)
stack.append(givenVertex)
# <- function to check cycle using DFS ->
def cycleCheckDFS(self,givenV,visited,currStack,vertList):
visited.add(givenV)
currStack.append(givenV)
for nbr in vertList[givenV]:
# nbr = vertList[givenV]
if nbr in currStack:
return True
else:
if nbr not in visited:
cycleMila = self.cycleCheckDFS(nbr,vertList)
if cycleMila is True:
return True
currStack.pop()
return False
# <- the function called by the driver ->
def isCircle(self,N,A):
for item in A:
first = item[0]
last = item[-1]
self.addEdges(first,last)
r1 = A[0]
givenV = r1[-1]
stack = []
visited1 = set()
list1 = [ v for v in self.vertList]
for vertices in list1:
if vertices not in visited1:
self.finishedTimeDFS(vertices,stack)
reversedGraph = self.reverseTheGraph()
visited2 = set()
ans = []
count = 0
while len(stack)>0:
curr = stack.pop()
if curr not in visited2:
reversedGraph.DFS(curr,ans)
count += 1
if count == 1:
# <- CHECK FOR CONNECTED COMPONENT HERE ->
r1 = A[0]
givenV = r1[-1]
visited3 = set()
currStack = []
result = self.cycleCheckDFS(givenV,visited3,self.vertList)
if result == True:
return 1
else:
return 0
else:
return 0
链接到在线评委 -> LINK