问题描述:
假设所有字符都是小写字母,长字符串是str。arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次。使用arr中的单词有多少种拼接str的方式,返回方法数。
思想
思想一:使用暴力的方法,整体的思想就是从index位置开始在单词表中找到多少种拼接方式返回。若index已经来到了字符串的结尾位置,那么说明前面都是有效的匹配获得一种拼接方式。从index开始到字符串的结尾,枚举每一个前缀串是否匹配单词表。
思想二:在思想一的基础上使用动态规划的想法。
思想三:利用前缀树进行加速查询,并且可以提前结束后序不能匹配的前缀。
代码
思想一代码:
public static int way1(String str, String[] arr) {
if (str == null || str.length() == 0 || arr == null || arr.length == 0) {
return 0;
}
HashSet<String> set = new HashSet<>();
for (String candidate : arr) {
set.add(candidate);
}
return process(str, 0, set);
}
//所有贴纸,都放在了set中
//str[i...]能够被set中的贴纸分解的话,返回分解的方法数
public static int process(String str, int i, HashSet<String> set) {
if (i == str.length()) {
return 1;
}
int ways = 0;
//[i...end]前缀串,每一个前缀串
for (int end = i; end < str.length(); end++) {
String pre = str.substring(i, end + 1);
if (set.contains(pre)) {
ways += process(str, end + 1, set);
}
}
return ways;
}
思想二代码:
public static int ways2(String str, String[] arr) {
if (str == null || str.length() == 0 || arr == null || arr.length == 0) {
return 0;
}
HashSet<String> map = new HashSet<>();
for (String s : arr) {
map.add(s);
}
int N = str.length();
int[] dp = new int[N + 1];
dp[N] = 1;
for (int i = N - 1; i >= 0; i--) {
for (int end = i; end < N; end++) {
if (map.contains(str.substring(i, end + 1))) {
dp[i] += dp[end + 1];
}
}
}
return dp[0];
}
思想三代码:
public static class Node {
public boolean end;
public Node[] nexts;
public Node() {
end = false;
nexts = new Node[26];
}
}
public static int ways3(String str, String[] arr) {
if (str == null || str.length() == 0 || arr == null || arr.length == 0) {
return 0;
}
Node root = new Node();
for (String s : arr) {
char[] chs = s.toCharArray();
Node node = root;
int index = 0;
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;
}
return g(str.toCharArray(), root, 0);
}
public static int g(char[] str, Node root, int index) {
if (index == str.length) {
return 1;
}
int ways = 0;
Node cur = root;
for (int end = index; end < str.length; end++) {
int path = str[end] - 'a';
if (cur.nexts[path] == null) {
break;
}
cur = cur.nexts[path];
if (cur.end) {
ways += g(str, root, end + 1);
}
}
return ways;
}