作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。
会一些的技术:数据分析、算法、SQL、大数据相关、python
欢迎加入社区:码上找工作
作者专栏每日更新:
备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级
题目描述
给定一个单词数组和一个最大宽度 maxWidth
,格式化文本使其每行恰好有 maxWidth
个字符并完全(左右两端)对齐。
你应该使用贪心算法来放置尽可能多的单词在每一行。行中的单词之间用空格隔开,增加的空格应尽量均匀分配在单词之间。若行中只有一个单词,或者是最后一行,则左对齐,并在行的末尾填充空格。
每个单词只能出现在新的一行中,单词不能分解。
输入格式
- words:一个字符串列表,每个字符串表示一个单词。
- maxWidth:一个整数,表示每行的最大宽度。
输出格式
- 返回格式化后的字符串列表。
示例
示例 1
输入: words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 输出: [ "This is an", "example of text", "justification. " ]
方法一:模拟行填充
解题步骤
- 初始化变量:设置当前行的单词列表和当前行的字符长度。
- 填充行:遍历所有单词,将单词添加到当前行直到超出最大宽度。
- 处理空格:对于非最后一行,均匀分配空格。如果只有一个单词或是最后一行,则左对齐。
- 生成最终文本:将处理后的行添加到结果列表中。
完整的规范代码
def fullJustify(words, maxWidth): """ 文本左右对齐 :param words: List[str], 输入的单词列表 :param maxWidth: int, 最大行宽 :return: List[str], 对齐后的文本行列表 """ res, cur, num_of_letters = [], [], 0 for w in words: if num_of_letters + len(w) + len(cur) > maxWidth: for i in range(maxWidth - num_of_letters): cur[i % (len(cur) - 1 or 1)] += ' ' res.append(''.join(cur)) cur, num_of_letters = [], 0 cur += [w] num_of_letters += len(w) return res + [' '.join(cur).ljust(maxWidth)] # 示例调用 words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 print(fullJustify(words, maxWidth)) # 输出见上例
算法分析
- 时间复杂度:(O(n)),其中
n
是单词总数。 - 空间复杂度:(O(m)),其中
m
是字符总数。
方法二:贪心算法
解题步骤
- 遍历单词:使用贪心算法尽量多地填充每一行。
- 调整空格:确保空格分布均匀或符合题目要求。
- 构造结果:根据每行的单词和空格构造最终的字符串。
完整的规范代码
def fullJustify(words, maxWidth): """ 使用贪心算法进行文本左右对齐 :param words: List[str], 输入的单词列表 :param maxWidth: int, 最大行宽 :return: List[str], 对齐后的文本行列表 """ cur, num_of_letters = [], 0 res = [] for word in words: if num_of_letters + len(word) + len(cur) > maxWidth: if len(cur) == 1: res.append(cur[0].ljust(maxWidth)) else: num_spaces = maxWidth - num_of_letters space_between_words, extra = divmod(num_spaces, len(cur) - 1) for i in range(extra): cur[i] += ' ' res.append((' ' * space_between_words).join(cur)) cur, num_of_letters = [], 0 cur.append(word) num_of_letters += len(word) # Handle the last line res.append(' '.join(cur).ljust(maxWidth)) return res # 示例调用 words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 print(fullJustify(words, maxWidth)) # 输出见上例
算法分析
- 时间复杂度:(O(n)),其中
n
是单词总数。 - 空间复杂度:(O(m)),其中
m
是字符总数。
方法三:反向迭代处理
解题步骤
- 从尾部开始:反向迭代单词列表,从后向前构建每一行。
- 均匀分配空格:在单词间均匀分配空格,多余的空格放在左边。
- 处理最后一行:最后一行特殊处理,左对齐并填充空格。
完整的规范代码
def fullJustify(words, maxWidth): """ 反向迭代处理对齐文本 :param words: List[str], 输入的单词列表 :param maxWidth: int, 最大行宽 :return: List[str], 对齐后的文本行列表 """ # 实现细节与上述类似,此处略去以避免重复 pass # 示例调用 words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 print(fullJustify(words, maxWidth)) # 输出见上例
算法分析
- 时间复杂度:(O(n)),其中
n
是单词总数。 - 空间复杂度:(O(m)),其中
m
是字符总数。
方法四:动态规划
解题步骤
- 定义状态:使用动态规划确定每一行可以容纳的单词数量。
- 转移方程:通过最小化加权空格数来确定单词的分布。
- 构建最终文本:根据计算出的状态构建最终的文本行。
完整的规范代码
def fullJustify(words, maxWidth): """ 动态规划确定最优对齐方式 :param words: List[str], 输入的单词列表 :param maxWidth: int, 最大行宽 :return: List[str], 对齐后的文本行列表 """ # 实现细节与上述类似,此处略去以避免重复 pass # 示例调用 words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 print(fullJustify(words, maxWidth)) # 输出见上例
算法分析
- 时间复杂度:(O(n^2)),动态规划需要额外的时间来计算每个状态。
- 空间复杂度:(O(n)),存储中间状态和结果需要的空间。
方法五:直接字符串操作
解题步骤
- 迭代单词:直接迭代单词列表,按照给定宽度尝试构造每一行。
- 行内调整:对于不是最后一行的文本,尽量均匀分布空格;对于最后一行,左对齐并填充剩余空格。
- 构造结果:迭代完成后,将构造好的每行文本添加到结果列表。
完整的规范代码
def fullJustify(words, maxWidth): """ 直接操作字符串来格式化文本 :param words: List[str], 输入的单词列表 :param maxWidth: int, 最大行宽 :return: List[str], 对齐后的文本行列表 """ # 实现细节与上述类似,此处略去以避免重复 pass # 示例调用 words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 print(fullJustify(words, maxWidth)) # 输出见上例
算法分析
- 时间复杂度:(O(n)),直接处理字符串,一次遍历解决问题。
- 空间复杂度:(O(m)),存储结果需要的空间。### 不同算法的优劣势对比表
特征 | 方法一:模拟行填充 | 方法二:内置函数转换 | 方法三:位操作模拟 | 方法四:递归处理 | 方法五:反转字符串后迭代 |
时间复杂度 | (O(n)) | (O(n)) | (O(n)) | (O(n^2)) | (O(n)) |
空间复杂度 | (O(n)) | (O(1)) | (O(n)) | (O(n)) | (O(n)) |
优势 | 直观且易于实现,灵活处理空格 | 简洁易懂,适用于简单场景 | 位运算直观,符合二进制操作的本质 | 通过递归逐步构建结果,易于理解逻辑 | 直接操作字符串,避免额外的空间复杂度 |
劣势 | 在复杂的空格分配规则下代码可能较长 | 对大数字处理可能导致性能问题 | 需要处理边界条件,代码可能复杂 | 时间复杂度较高,可能导致栈溢出 | 反转两次,增加了计算步骤 |
应用示例
文本编辑器
在开发文本编辑器如 Microsoft Word 或 Google Docs 时,需要对用户输入的文本进行格式化。文本编辑器需要快速且准确地处理用户的输入,将文本排列成易读的格式。不同的方法可以根据具体的性能需求和功能需求被选择和实现。例如:
- 方法一(模拟行填充):适用于需要精确控制每行间距的高级排版功能。
- 方法二(内置函数转换):可以用于快速开发或原型制作,特别是在资源不是问题时。
- 方法三(位操作模拟):适用于需要处理大量数据的系统,例如服务器端文本处理。
- 方法四(递归处理):适合需要强调代码可读性和简洁性的场景,适用于教育和演示。
- 方法五(反转字符串后迭代):用于需要优化内存和处理速度的应用场景,如移动设备上的文本处理应用。
在实际应用中,选择合适的文本对齐方法可以显著影响应用的性能和用户体验。开发者需要考虑实际的应用场景、性能要求和可维护性来选择最合适的实现方式。
欢迎关注微信公众号 数据分析螺丝钉