编程笔记:字符串变换问题 | 豆包MarsCode AI刷题

113 阅读4分钟

问题描述

小R拿到一个长度为 nn 的字符串,并希望将其转换为一个矩阵。这个矩阵有 xx 行 yy 列,满足 x×y=nx×y=n,即矩阵的每行有 yy 个字符。

矩阵的权值定义为其连通块的数量。两个字符相同且在矩阵中的上下左右相邻,则它们属于同一个连通块。小R希望通过合理地选择 xx 和 yy,使得矩阵的连通块数量最小。

你的任务是帮小R计算出这个最小的连通块数量。

题目解析

题目要求我们将一个长度为 n 的字符串转化为一个矩阵,并使得矩阵的连通块数量最小。矩阵的行数 x 和列数 y 满足 x * y = n。我们需要计算不同形状的矩阵中的连通块数量,然后选择连通块数量最小的情况返回。

连通块的定义

  • 连通块是指矩阵中,所有相邻且相同的字符组成的一个区域(上下左右相邻)。

思路分析

  1. 矩阵形状的选择
    由于矩阵的行数和列数需要满足 x * y = n,我们可以枚举所有满足这个条件的 xy。对于每一对 (x, y),我们都能构建一个矩阵。
  2. 矩阵构建
    给定 xy,将字符串按照行优先的顺序填充到一个 xy 列的矩阵中。
  3. 计算连通块数量
    连通块的计算可以通过深度优先搜索(DFS)实现。具体地,遍历矩阵中的每个位置,若当前位置的字符未被访问过,则从该位置开始进行 DFS,标记所有与之相连的字符为已访问,并计数一个连通块。
  4. 选择最优解
    对所有合法的 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:当前矩阵的连通块数量。

总结

  1. 矩阵构建:我们通过字符串填充矩阵,构建所有可能的矩阵形状。
  2. 连通块计算:使用 DFS 来遍历矩阵并计算连通块数量。
  3. 最小化连通块数量:对于每一种可能的矩阵形状,计算其连通块数量,并返回最小值。

知识点总结

  1. DFS(深度优先搜索) :用于查找图中的连通分量,在二维矩阵中可以用来查找相邻的相同字符。
  2. 枚举因子:在解决矩阵大小的问题时,首先需要找出所有的因子(即 x 和 y),从而构建矩阵。
  3. 矩阵填充:字符串可以按照行优先填充到矩阵中。
  4. 优化技巧:通过枚举因子来减少不必要的计算,只有 x * y = n 的组合才是有效的。

时间复杂度分析

  1. DFS复杂度:每次DFS遍历一个矩阵,最坏情况下是O(n),因为每个字符都可能被访问一次。
  2. 枚举因子复杂度:枚举 x 的因子需要遍历 1 到 n,最坏情况是 O(n)。
  3. 整体复杂度:考虑到每次需要进行 DFS,因此整体复杂度为 O(n^2),因为我们需要对每个可能的矩阵大小进行计算。