考虑一下:你必须找到所有以第一个字符开头的子词,然后是第二个字符,然后是第三个字符......等等。
这可以写成递归算法,接受两个参数:
- “前缀”
- 子词在前缀之后
在第一次迭代中,前缀将是一个空字符串,您将逐渐用子词填充它并打印一个字符。
我可以向您展示其工作原理的最简单方法是代码 sn-p:
public void printAllSubWords(String prefix, String subword) {
for(int i = 0; i < subword.length(); i++) {
System.out.println(prefix + subword.charAt(i));
printAllSubWords(prefix + subword.charAt(i),
subword.substring(i + 1, subword.length()));
}
}
这是如何工作的?
首先,考虑一个长度为 2 的字符串:
printAllSubWords("", "ab");
执行顺序是这样的:
当i = 0:
-
System.out.println(prefix + subword.charAt(i)); 将被这样评估:
System.out.println("" + "ab".charAt(0)); 并将打印a
- 那么调用
printAllSubWords(prefix + subword.charAt(i), subword.substring(i + 1, subword.length()));就会是
printAllSubWords("" + 'a', "ab".substring(0 + 1, "ab".length()));,即:
printAllSubWords("a", "b");
- 现在,在第二遍中,
System.out.println(prefix + subword.charAt(i)); 将按如下方式计算:
System.out.println("a" + "b".charAt(0)); 并将打印 ab
- 那么,仍然在第二遍中,
printAllSubWords(prefix + subword.charAt(i), subword.substring(i + 1, subword.length())); 将是 printAllSubWords("a" + 'b', "b".substring(0 + 1, "ab".length()));,即:
printAllSubWords("ab", "");
- 在第三遍中,
for 不会被执行,因为这个新子字 ("") 的长度为零,所以我们返回到最顶层的调用。
当i = 1:
-
System.out.println(prefix + subword.charAt(i)); 将被这样评估:
System.out.println("" + "ab".charAt(1)); 并将打印 b
- 那么调用
printAllSubWords(prefix + subword.charAt(i), subword.substring(i + 1, subword.length()));就会是
printAllSubWords("" + 'b', "b".substring(0 + 1, "ab".length()));,即:
printAllSubWords("b", "");
- 在这个新的第二遍中,
for 不会被执行,因为这个新的子字 ("") 的长度为零,所以我们返回到最顶层的调用,这将结束执行。
尝试为一个三四个字符的单词编写执行序列,看看会发生什么。
希望这会有所帮助。
在您的评论中,您说您想将子词存储在一个数组中(而且您非常具体:您不想要一个列表,而是一个简单的数组)。这是可能的,但它有一些问题。
- 您需要事先了解数组需要多少条目。由于数组无法调整大小,因此您需要在事情开始之前进行计算。
老实说,我会建议您使用 List(特别是 ArrayList),但让我们看看是否可以计算数组的长度。
Word lenght | Number of subwords
------------+-------------------
1 | 1
2 | 3
3 | 7
4 | 15
5 | 31
This question and its accepted answer 提示我在一个长度为n 的单词中有多少个子词。我留给你弄清楚(提示:答案的最后一部分是子序列数量的关键,但它包括 empty 子序列)。
一种可能的解决方案是:
- 创建一个整数静态变量(一个类变量)来保存您正在执行的迭代。该数字从零开始,每次打印/存储子字时增加一个单位
- 在同一个类中,编写一个创建适当大小数组的方法。
- 修改上述方法,除了前缀和子词之外,还接收这个新创建的数组。
- 将
System.out.println() 的内容替换为将生成的子词存储到数组中的句子,使用我在步骤 1 中提到的静态变量作为索引。
- 再次调用该函数时,请务必同时传递数组。
我会在几个小时后回来编写代码示例,但我希望您先尝试自己解决它(另外,上面的链接让我想到了另一种解决此问题的方法不需要递归,我会在以后的编辑中包含它)
我之前告诉你的解决方案是这样的:
public class SubwordPrinter2
{
private static int index;
private static void generateSubwords(String prefix, String subword, String[] arr) {
String s;
for(int i = 0; i < subword.length(); i++) {
s = prefix + subword.charAt(i);
arr[index] = s;
index++;
generateSubwords(prefix + subword.charAt(i),
subword.substring(i + 1, subword.length()),
arr);
}
}
public static void generateAllSubwords(String word) {
index = 0;
String[] subwords = new String[(int)Math.pow(2, word.length()) - 1];
generateSubwords("", word, subwords);
for(String s : subwords) {
System.out.println(s);
}
}
}
另一种没有递归的解决方案
由于顺序很重要,您可以创建一个二进制标志序列,告诉您一个字符是否必须包含在子词中。像这样的:
String: abc
Flags: 001
010
011
100
101
110
111
这些是 二进制 字符串。所以算法是:
- 对于
1 和(2^n) - 1 之间的i(其中n 是单词的长度)
- 创建一个二进制字符串,用零填充与单词的长度相同。
- 对于二进制字符串中的每个
1,打印/存储匹配的字符。
代码:
public void createSubwords(String word) {
// As you can see, your array must have (2^n) - 1 entries
String[] subwords = new String[(int)Math.pow(2, word.length()) - 1];
String bin;
String fmt;
String subword;
for(int i = 1; i < Math.pow(2, word.length()); i++) {
// fmt will be used to format the binary string so it is
// left padded with zeros
fmt = "%0" + word.length() + "d";
// bin is the binary string
bin = String.format(fmt, Long.parseLong(Integer.toBinaryString(i)));
// Initialize the subword
subword = "";
// For each '1' in the binary string, add the matching character
// to the subword
for(int j = 0; j < bin.length(); j++) {
if(bin.charAt(j) == '1')
subword = subword + word.charAt(j);
}
// Store it in the array
subwords[i - 1] = subword;
}
// Print each subword
for(String s : subwords) {
System.out.println(s);
}
}
希望对你有帮助