思路:
使用递归当前元素从index开始到end是否可以组成一个目标字符串数组的的一个元素。如果可以继续,不可以跳过。最终返回方式种数,如果大于0为true,反之false。代码如下:
public static boolean wordBreak(String s, List<String> wordDict) {
if (s == null || wordDict == null || wordDict.isEmpty()) {
return false;
}
int way = process(s, 0, wordDict);
return way != 0;
}
private static int process(String s, int index, List<String> wordDict) {
// 当index==length的时候 说明匹配已经完成 是一种方式 返回1
if (s.length() == index) {
return 1;
}
int ways = 0;
// index...index
// index...index+1
// index...index+2
// index...end
for (int end = index; end < s.length(); end++) {
String str = s.substring(index, end + 1);
if (wordDict.contains(str)) {
ways += process2(s, end + 1, wordDict);
}
}
return ways;
}
process方法从0开始。end从index开始递增如果index到end组成一种元素,继续递归剩下的元素。直到index=length表示已经递归完成得到一种方式。
但是会有重复计算在里面,下面使用动态规划:
设置dp[i]表示:字符串 s
的第 i
个字符到字符串末尾的子字符串是否可以完全由 wordDict
中的单词组成。
初始化dp[N]为1 表示一个空字符串总是可以被表示(基本情况)。
代码如下:
public static boolean wordBreak03(String s, List<String> wordDict) {
// 检查字符串或字典是否为空,如果是,则直接返回false,因为无法进行任何有效的单词拆分
if (s == null || wordDict == null || wordDict.isEmpty()) {
return false;
}
// 获取输入字符串的长度
int N = s.length();
// 初始化动态规划数组,大小为N+1,用于存储从每个位置开始到字符串末尾的子串是否可以被完全分割
int[] dp = new int[N + 1];
// 空字符串是可以被任何字典拆分的,设置dp[N]为1表示空字符串总是可以被分割
dp[N] = 1;
// 从字符串的末尾开始向前遍历,以填充dp数组
for (int index = N - 1; index >= 0; index--) {
// 初始化当前索引下的拆分方式计数为0
int ways = 0;
// 尝试将字符串从当前索引index切分到不同的结束点end
for (int end = index; end < N; end++) {
// 截取从index到end的子字符串
String str = s.substring(index, end + 1);
// 如果字典中包含这个子字符串
if (wordDict.contains(str)) {
// 累加从end+1位置到字符串末尾的可分割性,增加当前索引的分割方法数量
ways += dp[end + 1];
}
}
// 将计算结果存储在dp数组中,dp[index]表示从index到末尾的字符串是否可以被完全分割
dp[index] = ways;
}
// 如果dp[0]不为0,表示整个字符串可以被字典中的单词完全分割,返回true;否则返回false
return dp[0] != 0;
}
每次截取其实也会费时间,还有没有别的方式呢?有的,trie前缀树,代码如下:
public static boolean wordBreak(String s, List<String> wordDict) {
if (s==null||wordDict==null||wordDict.isEmpty()){
return false;
}
Node root=new Node();
for (String str : wordDict) {
char[] chs = str.toCharArray();
int index;
Node node=root;
for (int i = 0; i < chs.length; i++) {
index=chs[i]-'a';
if (node.nexts[index]==null){
node.nexts[index]=new Node();
}
node=node.nexts[index];
}
node.end=true;
}
char[] str = s.toCharArray();
int N =str.length;
int[] dp = new int[N + 1];
dp[N]=1;
for (int i = N-1; i>=0 ; i--) {
Node cur=root;
for (int end = i; end <N ; end++) {
cur=cur.nexts[str[end]-'a'];
if (cur==null){
break;
}
if (cur.end){
dp[i]=dp[i]+dp[end+1];
}
}
}
return dp[0]!=0;
}
public static class Node {
public boolean end;
public Node[] nexts;
public Node() {
end = false;
nexts = new Node[26];
}
}