所以,我需要递归地找到给定字符串的所有子集。到目前为止我所拥有的是:
static ArrayList<String> powerSet(String s){
ArrayList<String> ps = new ArrayList<String>();
ps.add(s);
for(int i=0; i<s.length(); i++){
String temp = s.replace(Character.toString(s.charAt(i)), "");
ArrayList<String> ps2 = powerSet(temp);
for(int j = 0; j < ps2.size(); j++){
ps.add(ps2.get(j));
}
}
return ps;
我想我知道现在的问题是什么,但我不知道如何解决它。目前,我找到所有temp的幂集,分别是“bcd”,“acd”,“abd”,“abc”,这将导致重复。有想法该怎么解决这个吗?
通过powerset,我的意思是如果字符串是abc,它将返回“”,“a”,“b”,“c”,“ab”,“ac”,“bc”,“abc”。
答案 0 :(得分:2)
具有n个元素的集合的子集数是2 n 。例如,如果我们有字符串&#34; abc&#34;,我们将有2个 n = 2 3 = 8个子集。
可以用n位表示的状态数也是2 n 。我们可以证明枚举n位的所有可能状态与具有n个元素的集合的所有可能子集之间存在对应关系:
2 1 0 2 1 0
c b a bits
0 0 0 0
1 a 0 0 1
2 b 0 1 0
3 b a 0 1 1
4 c 1 0 0
5 c a 1 0 1
6 c b 1 1 0
7 c b a 1 1 1
例如,如果我们考虑第5行,则第2位和第0位是活动的。如果我们abc.charAt(0) + abc.charAt(2)
,我们会得到子集ac
。
为了枚举n位的所有可能状态,我们从0开始,总和1直到我们达到2 n - 1.在这个解决方案中,我们将从2 n 开始 - 1并递减到0,所以我们不需要另一个参数来保持子集的数量,但效果是一样的:
static List<String> powerSet(String s) {
// the number of subsets is 2^n
long numSubsets = 1L << s.length();
return powerSet(s, numSubsets - 1);
}
static List<String> powerSet(String s, long active) {
if (active < 0) {
// Recursion base case
// All 2^n subsets were visited, stop here and return a new list
return new ArrayList<>();
}
StringBuilder subset = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
// For each bit
if (isSet(active, i)) {
// If the bit is set, add the correspondent char to this subset
subset.append(s.charAt(i));
}
}
// Make the recursive call, decrementing active to the next state,
// and get the returning list
List<String> subsets = powerSet(s, active - 1);
// Add this subset to the list of subsets
subsets.add(subset.toString());
return subsets;
}
static boolean isSet(long bits, int i) {
// return true if the ith bit is set
return (bits & (1L << i)) != 0;
}
然后你只需要打电话:
System.out.println(powerSet("abc"));
获取所有8个子集:
[, a, b, ab, c, ac, bc, abc]
答案 1 :(得分:1)
我发现在设计递归算法时首先考虑简单的角点情况很有帮助,即空字符串和带有一个字符的字符串。然后你通常会分解问题,并在字符串的rest / tail上进行递归调用。有点像这样:
static List<String> nuPowerSet(String s) {
if (s.length() == 0) { // trivial, subset of empty string is empty
return emptyList();
}
String head = s.substring(0, 1);
if (s.length() ==1) // the subset of a one character string is exactly that character
return asList(head);
String tail = s.substring(1);
ArrayList<String> ps = new ArrayList<String>();
ps.add(head); // one of the subsets is the current first character
List<String> tailSubsets = nuPowerSet(tail); // all the subsets of the remainder.
List<String> tailSubsetsWithCurrentHeadPrepended = tailSubsets
.stream()
.map(element -> head + element)
.collect(Collectors.toList());
ps.addAll(tailSubsets);
ps.addAll(tailSubsetsWithCurrentHeadPrepended);
return ps;
}
答案 2 :(得分:1)
有一种方法可以在不使用递归的情况下完成此操作,它依赖于位串和子集之间的简单对应。
所以,假设你有一个三个字符串&#34; abc&#34;然后,如你所说,子集将是&#34;&#34;,&#34; c&#34;,& #34; b&#34;,&#34; bc&#34;,&#34; a&#34;,&#34; ac&#34;,&#34; ab&#34;,&#34; abc&# 34;
如果您创建一个字符表并为子集中的每个字符写1,而在子集中不为0,则可以看到一个模式:
a b c bits decimal
0 0 0 0
c 0 0 1 1
b 0 1 0 2
b c 0 1 1 3
a 1 0 0 4
a c 1 0 1 5
a b 1 1 0 6
a b c 1 1 1 7
对于每个长度为n的唯一字符串,您将拥有2个 n 子集,并且您可以通过简单地从i = 0到i =进行一个for
循环来生成所有这些子集。 2 n -1,仅包含与i
中1位相对应的字符。
答案 3 :(得分:1)
要消除重复,你只需将它们全部添加到Set
中,这可以通过某种帮助来轻松完成:
static ArrayList<String> powerSet(String s) {
return new ArrayList<>(_powerSet(s));
}
static HashSet<String> _powerSet(String s) {
HashSet<String> set = new HashSet<>();
set.add(s);
for(int i = 0; i < s.length(); i++) {
String tmp = s.substring(0, i) + s.substring(i+1, s.length());
set.addAll(_powerSet(tmp));
}
return set;
}
顺便说一下,你的代码本质上处理了边缘情况。你不用担心这个。
答案 4 :(得分:0)
你是对的,你确实有重复项,因为你多次创建temp
(每次没有其他字符)所以当你递归调用时会有不同的子集,它们将共享相同的字符和创建dup。例如,“abc”将创建一个temp
,其中包含:[“ab”,“ac”,“bc”],并且每个人都将仅使用一个字符递归调用,这样您就可以获得每个字符。 a“,”b“和”c“两次。
避免它的一种方法(使用最少的更改)将使用Set而不是列表 - 这将省略所有重复:
static Set<String> powerSet(String s) {
Set<String> ps = new HashSet<>();
ps.add(s);
for (int i = 0; i < s.length(); i++) {
String temp = s.replace(Character.toString(s.charAt(i)), "");
Set<String> ps2 = powerSet(temp);
for (String x : ps2) {
ps.add(x);
}
}
return ps;
}
现在输出将是:
bc
a
ab
b
ac
abc
c
另一种解决方案:
public static List<String> powerset(String s) {
List<String> ans = new LinkedList<>();
if (null == s) {
return ans;
}
return powerset(s, ans);
}
private static List<String> powerset(String s, List<String> ans) {
if ("".equals(s)) {
return ans;
}
String first = s.substring(0, 1);
String rest = s.substring(1);
ans.add(first);
List<String> pAns = new LinkedList<>(ans);
for (String partial : ans.subList(0, ans.size()-1)) {
pAns.add(partial + first);
}
return powerset(rest, pAns);
}
<强>输出强>
[a, b, ab, c, ac, bc, abc]
答案 5 :(得分:0)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MainClass {
static List<List<char[]>> list = new ArrayList<List<char[]>>();
// static List<int[]> list1 = new ArrayList<int[]>();
public static void main(String[] args) {
List<char[]> list1 = new ArrayList<char[]>();
String string = "abcd";
char[] a = string.toCharArray();
generate(a, 0, 0, list1);
for (List<char[]> l : list) {
for (char[] b : l) {
for (char c : b) {
System.out.print(c + ",");
}
System.out.println();
}
}
}
public static void generate(char[] array, int offset, int index, List<char[]> list1) {
if (offset >= array.length)
return;
char[] newArray = Arrays.copyOfRange(array, offset, index);
list1.add(newArray);
if (index >= array.length) {
list.add(list1);
offset++;
index = offset;
generate(array, offset, index, new ArrayList<char[]>());
} else {
index++;
generate(array, offset, index, list1);
}
}
}