问题描述
小R拿到一个长度为 nn 的字符串,并希望将其转换为一个矩阵。这个矩阵有 xx 行 yy 列,满足 x×y=nx×y=n,即矩阵的每行有 yy 个字符。
矩阵的权值定义为其连通块的数量。两个字符相同且在矩阵中的上下左右相邻,则它们属于同一个连通块。小R希望通过合理地选择 xx 和 yy,使得矩阵的连通块数量最小。
你的任务是帮小R计算出这个最小的连通块数量。
题目解析
题目要求我们将一个长度为 n 的字符串转化为一个矩阵,并使得矩阵的连通块数量最小。矩阵的行数 x 和列数 y 满足 x * y = n。我们需要计算不同形状的矩阵中的连通块数量,然后选择连通块数量最小的情况返回。
连通块的定义:
- 连通块是指矩阵中,所有相邻且相同的字符组成的一个区域(上下左右相邻)。
思路分析
- 矩阵形状的选择:
由于矩阵的行数和列数需要满足x * y = n,我们可以枚举所有满足这个条件的x和y。对于每一对(x, y),我们都能构建一个矩阵。 - 矩阵构建:
给定x和y,将字符串按照行优先的顺序填充到一个x行y列的矩阵中。 - 计算连通块数量:
连通块的计算可以通过深度优先搜索(DFS)实现。具体地,遍历矩阵中的每个位置,若当前位置的字符未被访问过,则从该位置开始进行 DFS,标记所有与之相连的字符为已访问,并计数一个连通块。 - 选择最优解:
对所有合法的x, y组合,我们计算连通块的数量,并返回最小的连通块数量。
代码详解
1. count_connected_components 函数
该函数用于计算矩阵中的连通块数量。通过 DFS 来标记所有与当前字符相连的相同字符,递归地遍历相邻的上下左右位置。
def count_connected_components(matrix, x, y):
visited = [[False] * y for _ in range(x)] # 创建访问标记矩阵
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 四个方向:上、下、左、右
# 深度优先搜索(DFS)函数
def dfs(i, j, char):
stack = [(i, j)] # 用栈来模拟递归
while stack:
ci, cj = stack.pop()
for di, dj in directions:
ni, nj = ci + di, cj + dj
if 0 <= ni < x and 0 <= nj < y and not visited[ni][nj] and matrix[ni][nj] == char:
visited[ni][nj] = True
stack.append((ni, nj))
count = 0
for i in range(x):
for j in range(y):
if not visited[i][j]: # 如果当前字符未访问过
count += 1 # 发现一个新连通块
visited[i][j] = True
dfs(i, j, matrix[i][j]) # 进行DFS,标记所有相连字符
return count
- visited:二维列表,用来记录每个位置是否已经访问过。
- directions:四个可能的方向,用来移动到上下左右的相邻位置。
- dfs(i, j, char) :从位置
(i, j)开始,查找与之相连的所有相同字符,并标记为访问过。
2. solution 函数
该函数用于计算最小连通块数量。
def solution(n: int, s: str) -> int:
min_connected_components = float('inf') # 初始值设为无穷大,寻找最小值
# 枚举所有可能的 x, y 组合
for x in range(1, n + 1):
if n % x == 0: # x 必须是 n 的因子
y = n // x # 计算对应的 y
# 构建矩阵
matrix = [list(s[i * y:(i + 1) * y]) for i in range(x)] # 按行填充字符串
# 计算连通块
connected_components = count_connected_components(matrix, x, y)
# 更新最小连通块数量
min_connected_components = min(min_connected_components, connected_components)
return min_connected_components
- n:字符串的长度。
- s:输入的字符串。
- x, y:矩阵的行数和列数,满足
x * y = n。 - matrix:构建的矩阵。
- connected_components:当前矩阵的连通块数量。
总结
- 矩阵构建:我们通过字符串填充矩阵,构建所有可能的矩阵形状。
- 连通块计算:使用 DFS 来遍历矩阵并计算连通块数量。
- 最小化连通块数量:对于每一种可能的矩阵形状,计算其连通块数量,并返回最小值。
知识点总结
- DFS(深度优先搜索) :用于查找图中的连通分量,在二维矩阵中可以用来查找相邻的相同字符。
- 枚举因子:在解决矩阵大小的问题时,首先需要找出所有的因子(即
x和y),从而构建矩阵。 - 矩阵填充:字符串可以按照行优先填充到矩阵中。
- 优化技巧:通过枚举因子来减少不必要的计算,只有
x * y = n的组合才是有效的。
时间复杂度分析
- DFS复杂度:每次DFS遍历一个矩阵,最坏情况下是O(n),因为每个字符都可能被访问一次。
- 枚举因子复杂度:枚举
x的因子需要遍历1到n,最坏情况是 O(n)。 - 整体复杂度:考虑到每次需要进行 DFS,因此整体复杂度为 O(n^2),因为我们需要对每个可能的矩阵大小进行计算。