以下是LeetCode问题139"单词拆分"(Word Break)的Java解法,使用了动态规划(Dynamic Programming, DP)来解决这个问题:
java
import java.util.List;
import java.util.Set;
import java.util.HashSet;
public class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
// 将wordDict转化为一个Set,以加快查找速度
Set<String> wordSet = new HashSet<>(wordDict);
// 创建一个DP数组,dp[i]表示字符串s的前i个字符是否可以由字典中的单词拼接而成
boolean[] dp = new boolean[s.length() + 1];
// 空字符串可以被认为是可拼接的
dp[0] = true;
// 动态规划填充dp数组
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
// 如果s的前j个字符可拼接,且[j, i)这部分字符在字典中
if (dp[j] && wordSet.contains(s.substring(j, i))) {
dp[i] = true;
break; // 找到一个可拼接方案后就可以跳出当前循环
}
}
}
// dp[s.length()]就是答案,表示整个字符串s是否可拼接
return dp[s.length()];
}
}
解决思路:
-
使用动态规划:
- 定义一个布尔数组
dp[]
,其中dp[i]
表示从字符串的开头到索引i-1
的子字符串是否可以用字典中的单词拆分出来。 dp[0]
表示空字符串,因此我们初始化dp[0] = true
。
- 定义一个布尔数组
-
递推关系:
- 对于每个
i
,我们检查j
从0
到i
的所有位置,如果dp[j]
为true
并且s[j:i]
存在于wordDict
中,那么我们设置dp[i] = true
,并跳出内部循环。
- 对于每个
-
时间复杂度:
- 由于每个子串的生成和查找都可能花费 O(n) 的时间,总体时间复杂度为 O(n^2),其中 n 是字符串
s
的长度。
- 由于每个子串的生成和查找都可能花费 O(n) 的时间,总体时间复杂度为 O(n^2),其中 n 是字符串
java solution
java
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
//将List<String> wordDict转化为HashSet, wordDict中的string作为hashset中的key
Set<String> wordSet = new HashSet<>(wordDict);
//dp[i] 表示s的前i个字符(下标0~i-1)构成的子串是否可以成功拆分
//这里之所以需要创建s.length() + 1个数组单元是因为s.length() + 1个数组单元的下标范围是[0,s.length()]
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
//将子串(0,i)拆分为2部分,(0,j)和(j,i), 下标都是左闭右开。
// 如果(0,j)这一部分可以成功拆分并且另一部分(j,i)在字典中,说明子串(0,i)是可以成功被拆分的
for(int i = 0; i <= s.length(); ++i) {
for(int j = 0; j < i; ++j) {
if(dp[j] == true && wordSet.contains(s.substring(j, i))) {
dp[i] = true;
break; // break 只能跳出当前的 内部 for 循环
}
}
}
return dp[s.length()];
}
}