我正在尝试了解如何查找给定字符串的所有可能组合(子字符串)。我想到了一个有效的算法,它基本上就像这样:
示例:"abc"
"abc"
添加到输出"bc"
) - 添加到输出,然后第二个("ac"
) - 添加到输出,然后删除第三个("ab"
) - 添加到输出。"a"
,"b"
,"c"
)并添加到输出现在,我不知道我将如何写这个,所以我要求一点帮助,没有什么先进的,因为这是我的hw,我想自己学习和做。更具体地说,我想知道如何在不改变输入的情况下从中间删除char。
另外,"cb"
对我来说不是一个子词,因为所有子词都是在原始字符串中显示的字符顺序。
答案 0 :(得分:3)
考虑一下:您必须找到所有以第一个字符开头的子字,然后是第二个字符,然后是第三个字符......依此类推。
这可以写成一个递归算法,有两个参数:
在第一次迭代中,前缀将是一个空字符串,您将逐渐用子字填充它并打印一个字符。
我可以向您展示其工作原理的最简单方法是代码段:
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
的单词中有多少个子字的提示。我留给你弄明白了(提示:答案的最后部分是子序列数量的关键,但它包括空子序列)。
一种可能的解决方案是:
System.out.println()
内容与存储生成的子词的句子替换为数组。我会在几个小时后回来编写代码示例,但我希望您先尝试自己解决(另外,上面的链接让我想到了另一种方法解决这个不需要递归的问题,我将在以后的编辑中将其包括在内)
我之前告诉过你的解决方案是这样的:
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
这些是二进制字符串。所以算法将是:
i
与1
之间的(2^n) - 1
(其中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);
}
}
希望这有帮助
答案 1 :(得分:2)
我已经在Iterator<T>
中实现了此功能,这可以实现内容的延迟生成。
import java.math.BigInteger;
import java.util.Iterator;
public class SubstringIterator implements Iterator<String> {
String s;
BigInteger cur = BigInteger.ZERO;
BigInteger max;
public SubstringIterator(String s) {
this.s = s;
max = BigInteger.ONE.shiftLeft(s.length()).subtract(BigInteger.ONE);
}
@Override
public boolean hasNext() {
return cur.compareTo(max) < 0;
}
@Override
public String next() {
cur = cur.add(BigInteger.ONE);
StringBuilder sb = new StringBuilder();
for(int i = 0x00; i < s.length(); i++) {
if(cur.testBit(i)) {
sb.append(s.charAt(i));
}
}
return sb.toString();
}
@Override
public void remove() {
throw new UnsupportedOperationException("This is not a collection iterator");
}
}
代码的工作原理如下:您需要声明一个bitarray:一个具有任意位数的数组。现在我们使用BigInteger
,因为它非常方便,但您可以使用任何等效的数据结构。
bitarray维护一个位列表。当 i -th位为1
时,这意味着相应的字符应该在要生成的字符串中,因此如果字符串是foobar
且状态为{{ 1}},结果将是:
011011
因此foobar
011011
oo ar
。基于bitarray生成String的过程由:
ooar
现在唯一缺少的是迭代具有该长度的一组比特阵列。为此,StringBuilder sb = new StringBuilder();
for(int i = 0x00; i < s.length(); i++) {
if(cur.testBit(i)) {
sb.append(s.charAt(i));
}
}
return sb.toString();
提供的方法很有用。这将进行二进制增量。但是,您可以使用Gray counter。在这种情况下,输出的顺序会有所不同,但这不是主要问题。
所以我们就是这样设置BigInteger
来表示状态。最初状态为current
,因此为空字符串。但我们不需要发出这种状态。
在00000...000
方法中,我们检查hasNext
是否已达到可能性的终点。这是状态为Iterator<T>
的时候。因此,我们将最大值存储在11111....111
中,其中 n 次max
,其中1
为字符串的长度。
最后,n
方法只需递增状态并计算结果。
现在你可以生成一个包含结果的数组。但总的来说,next
更好。迭代器不会显式存储所有值。因此,内存使用率(几乎)不变,而数组会导致指数内存使用。
此外,它可以节省CPU使用率,因为人们并不总是需要计算所有值。假设您正在查看Iterator<T>
是否是成员,您可以从生成foo
的那一刻起切断搜索,而首先构建整个数组可能会更加昂贵。
参见在线演示here。
如果空字符串也被视为子字符串替换:
"foo"
通过
BigInteger cur = BigInteger.ZERO;
答案 2 :(得分:0)
我做了一个递归函数。它看起来像这样
这不是可编译的java代码。它只概述了算法
List<String> GetSubwords(String str)
{
if(str.length == 1)
return str;
List<String> result = new List<String>();
FirstChar = str[0];
// the portion of the string after the first character
var smallString = str.Substring(1, str.length-1);
List<String> smallerSubWords = GetSubwords(smallString);
result.add(FirstChar.ToString())
foreach(subword in smallerSubwords)
{
result.add(subword);
result.add(firstChar + subword);
}
return result;
}
这基本上需要一个字符串,比如&#34; ABCD&#34;,删除&#34; A&#34;,然后获取&#34; BCD&#34;的所有子字的列表,并返回那些列表,以及前面加'A'
的列表
答案 3 :(得分:0)
这是一个简单的python版本的递归,java中的翻译可能会很冗长,但很简单:
def subs(s):
if len(s) == 0:
return ['']
return [pref + sb for sb in subs(s[1:]) for pref in ('', s[0])]
print subs('ABC')
答案 4 :(得分:0)
这是一个简单的算法。 Say string的长度为n
。生成从0
到2^n-1
的所有数字。对于每个这样的数字,如果第i位设置为1,则从左到右扫描其二进制表示并写入ith字符输出。
以下是可以转换为java的C ++示例:
char s[] = "abc";
for(int i = 0; i < 1 << 3; i++)
{ for(int j = 0; j < 32; j++)
{ if((1 << j) & i)
printf("%c", s[j]);
}
puts("");
}