Word Break Problem in Java

2025年5月3日 | 阅读 9 分钟

单词拆分问题 (Word Break Problem) 是指判断一个给定的字符串是否可以被拆分成若干个有效的单词,而这些单词都存在于一个给定的字典中。目标是确定该字符串是否可以从字典中的单词列表里分割成一个或多个单词。这个问题可以通过多种方法来解决,包括递归、回溯或动态规划。解决方案涉及检查有效的词首,并递归地检查字符串的剩余部分是否也能被分割成字典中的单词。

字典

{ i, like, sam, sung, samsung, mobile, ice, cream, icecream, man, go, mango }

示例 1

输入: ilikeicecream

输出:

该字符串可以被分割为 “i like ice cream” 或 “i likeicecream”。

示例 2

输入: mangoicecream

输出:

该字符串可以被分割为 “mango ice cream” 或 “mangoicecream”。

示例 3

输入: samsungmobile

输出:

该字符串可以被分割为 “samsung mobile”。

使用递归方法

单词拆分问题的递归方法涉及将目标 字符串 拆分成词首,并检查每个词首是否存在于字典中。如果找到一个词首,函数会递归地检查剩余的子字符串,并持续这个过程,直到整个字符串都被分割,或者所有可能性都已用尽,从而导致成功或失败。

算法

步骤 1: 以字典(单词列表)和目标(要分割的字符串)作为输入。

步骤 2: 如果目标字符串为空,则返回 true(空字符串被视为已分割)。

步骤 3: 对于从 1 到目标字符串长度的每个索引 i,提取词首子字符串。

步骤 4: 如果词首在字典中,则递归地检查剩余字符串是否可以被分割。

步骤 5: 如果任何递归调用导致成功分割,则返回 true;否则,返回 false。

让我们在 Java 程序中实现上述方法。

文件名: WordSegmenter.java

输出

 
true   

时间复杂度: O(2^N), 因为每个词首都会产生指数级的递归调用。

辅助空间: O(N), 用于递归栈深度,最多为目标字符串的长度。

使用动态规划

单词拆分问题的动态规划方法涉及创建一个布尔 数组 来存储子字符串是否可以被分割成有效的字典单词。通过遍历字符串并利用先前计算的结果,该方法优化了分割检查,并有效地确定整个字符串是否可以由字典中的单词组成。

算法

步骤 1: 如果输入字符串为空,则返回 true,因为它始终可以被分割。

步骤 2: 初始化一个大小为 inputString.length() + 1 的布尔数组 dpTable,用于存储子问题的结果。

步骤 3: dpTable[i] 为 true,表示子字符串 inputString[0..i-1] 可以被分割成字典中的单词。

步骤 4: 遍历输入字符串的每个词首 inputString[0..i-1],检查它是否存在于字典中。如果词首存在,则将 dpTable[i] 标记为 true。

步骤 5: 如果 dpTable[i] 为 true,则检查剩余的子字符串 inputString[i..end]。继续检查并更新 DP 表中每个有效的子字符串,直到整个字符串被分割。

步骤 6: 如果 DP 表的最后一个位置 (dpTable[strLength]) 为 true,则表示成功分割,返回 true;否则,返回 false。

让我们在 Java 程序中实现上述方法。

文件名: StringSegmentation.java

输出

 
Yes
Yes
Yes
Yes
Yes
No   

时间复杂度: O(N² * M), 其中 N 是输入长度,M 是字典单词的平均长度。

空间复杂度: O(N), 用于存储词首分割结果的 DP 表。

优化的动态规划方法

单词拆分问题的优化 动态规划 解决方案使用布尔 DP 数组来存储子字符串是否可以分割成字典单词,从而避免了冗余检查。它遍历有效的起始点,并且只处理已经有有效先前分割的子字符串,从而提高了效率。

算法

步骤 1: 创建一个大小为 n+1 的布尔数组 segmentation[](其中 n 是输入字符串的长度)。

步骤 2: 设置 segmentation[0] = true,因为空字符串始终可以被分割。

步骤 3: 使用索引 end 从 1 到 n(字符串长度)遍历字符串。

  • 对于每个 end,遍历所有可能的起始点,从 0 到 end - 1。
  • 检查子字符串 str[start..end] 是否在字典中,并且 segmentation[start] 是否为 true。

步骤 4: 如果两个条件都满足,则设置 segmentation[end] = true 并中断当前 end 的内层循环。

步骤 5: 填充完 segmentation 数组后,返回 segmentation[n] 的值,以检查整个字符串是否可以被分割。

让我们在 Java 程序中实现上述方法。

文件名: WordSegmenter.java

输出

 
Yes
Yes
Yes
Yes
Yes
Yes