给定字符串JAVA中所有可能的不同字符子集

时间:2016-01-22 00:12:57

标签: java string function recursion subset

正如标题所说,我需要做以下事情。但我在某种程度上得到了错误的答案,或许有些循环是错误的?

这是我到目前为止编码的内容,但它似乎给了我错误的结果。任何想法,帮助,提示,修复?

import java.util.ArrayList;

public class pro1
{
private String lettersLeft;
private ArrayList<String> subsets;

public pro1(String input)
{
    lettersLeft = input;
    subsets = new ArrayList<String>();
}

public void createSubsets()
{       
    if(lettersLeft.length() == 1)
    {
        subsets.add(lettersLeft);
    }
    else
    {
        String removed = lettersLeft.substring(0,1);
        lettersLeft = lettersLeft.substring(1);
        createSubsets();
        for (int i = 0; i <= lettersLeft.length(); i++)
        {
            String temp = removed + subsets.get(i);
            subsets.add(temp);
        }
        subsets.add(removed);
    }
}

public void showSubsets()
{
    System.out.print(subsets);
}
}

我的测试课在这里:

public class pro1
{
public static void main(String[] args) 
{
    pro1s = new pro1("abba");
    s.createSubsets();
    s.showSubsets();
}
}

2 个答案:

答案 0 :(得分:0)

尝试

int numSubsets = (int)java.lang.Math.pow(2,toSubset.length());
for (int i=1;i<numSubsets;i++) {
    String subset = "";
    for (int j=0;j<toSubset.length();j++) {
        if ((i&(1<<j))>0) {
            subset = subset+toSubset.substring(j,j+1);
        }
    }
    if (!subsets.contains(subset)) {
        subsets.add(subset);
    }
}

其中toSubset是您希望子集化的字符串(示例中为String toSubset="abba"),subsets是包含结果的ArrayList。

为此,我们实际迭代功率集(所有子集的集合),其大小为2 ^ A,其中A是原始集合的大小(在本例中是字符串的长度)。

每个子集可以用0到2 ^ A-1的数字唯一地标识,其中第j位(0索引)的值指示该元素是否存在,其中1表示存在,0表示不存在。注意,数字0表示二进制字符串00 ... 0,其对应于空集。因此,我们从1开始计数(您的示例未将空集显示为所需的子集)。

对于每个值,我们通过查看每个位位置并使用逐位算术确定它是1还是0来构建子集字符串。 1<<j是在第j个二进制位置中为1的整数,i&(i<<j)是仅在整数具有1的位置中的1的整数(因此如果我有1则为0或1)在第j个二进制数字)。如果我在第j个二进制数字中有1,我们追加字符串的第j个元素。

最后,当您要求使用唯一子集时,我们检查是否已经使用了该子集,如果没有,我们将其添加到ArrayList。

答案 1 :(得分:0)

使用递归时很容易让你的头转过来。一般来说,我怀疑你的问题是,你在寄存兔子洞的路上存储的一个字符串用于备份的方式是一个类成员变量,你的递归方法是同一个类的方法。尝试在createSubsets()方法中使lettersLeft成为局部变量。类似的东西:

public class Problem1
{
private String originalInput;
private ArrayList<String> subsets;

public Problem1(String input)
{
    originalInput = input;
    subsets = new ArrayList<String>();
}

// This is overloading, not recursion.
public void createSubsets()
{    
    createSubsets(originalInput);
}
public void createSubsets(String in)
{       
    if(in.length() == 1)
    {
        // this is the stopping condition, the bottom of the rabbit hole
        subsets.add(in);
    }
    else
    {
        String removed = in.substring(0,1);
        String lettersLeft = in.substring(1);

        // this is the recursive call, and you know the input is getting
        // smaller and smaller heading toward the stopping condition
        createSubsets(lettersLeft);

        // this is the "actual work" which doesn't get performed
        // until after the above recursive call returns
        for (int i = 0; i <= lettersLeft.length(); i++)
        {
            // possible "index out of bounds" here if subsets is 
            // smaller than lettersLeft
            String temp = removed + subsets.get(i);
            subsets.add(temp);
        }
        subsets.add(removed);
    }
}

当你在代码中试图思考它将如何运行时需要记住的事情...你已经构建了递归方法,使得执行指针在执行任何“实际工作”之前一直沿着递归兔子洞进行“,只需从输入中拉出字母并将它们推入堆栈即可。所有“真正的工作”都是从兔子洞里回来的,而字母从堆栈中弹出。因此,子集列表中的第一个'a'实际上是输入字符串'abba'中的最后一个'a'。 I.E.添加到子集列表的第一个字母是lettersLeft.length() == 1。 (我的例子中是in.length() == 1)。此外,调试器是你的朋友。步骤调试是一种很好的方法,可以验证您的代码实际上正在按照您希望在整个过程中的每个步骤执行的操作。