给定整数到字符的映射找到给定整数的所有可能的字符组合

时间:2014-07-29 08:40:52

标签: java algorithm

我有一个包含从整数到字符的映射的库。我也有一个整数形式的字符串。我正在尝试编写一个方法,在解释字符串时将返回所有可能的字符组合。例如: 给定以下库a=1, b=2, c=3, k=11和字符串"1123",输出应该是包含"aabc", "kbc"的列表。假设可以在库中找到所有给定的数字。

我目前的解决方案如下:

public static ArrayList<String> d(String s) {
    ArrayList<String> s2 = new ArrayList<String>();
    if (s.length() == 1) {
        s2.add(""+library.get(Integer.valueOf(s)));
        return s2;
    }
    for (int i=0; i < s.length(); i++) {
        String curr = s.substring(0, i + 1);
        if (library.containsKey(Integer.valueOf(curr))){
            ArrayList<String> strings = d(s.substring(i + 1));
            char c2 = library.get(Integer.valueOf(curr));
            for (String tmp : strings){
                s2.add(c2 + tmp);
            }
        }
    }
    return s2;
}

有没有更优化的方法来解决这个问题?还有什么是我的解决方案的复杂性?我的假设是O(N ^ 3)时间。

3 个答案:

答案 0 :(得分:2)

您的问题映射到以下语法:

S -> SA | SB | SC | SK | ε
A -> 1
B -> 2
C -> 3
K -> 11

这是一个无上下文的语法,这意味着任何体面的解析器(CYKEarley)都会在O(n 3 )时解析它作为最坏的情况场景。任何比这更糟的事情,你肯定是在错误的轨道上。

(注意:虽然语法是无上下文的,但它定义的语言实际上是规则的。增加的复杂性来自于我们生成所有可能的解析树的要求。如果要求只是决定一个整数是否是我们语言中的有效句子,正则表达式((1)|(2)|(3)|(11))+就够了)

答案 1 :(得分:1)

这个问题让我想起一个据称在Google采访中被问到的人:

  

编写一个程序来计算所有不同的方法来建造一个N高度乐高塔,其长度和宽度为1,高度由一个集合给出(即[1,2,3,4])

在我们的案例中,library是集合,我们必须考虑一个额外的约束:一个片段必须与文本匹配。

由此,我们可以尝试动态编程解决方案来计算可能的“建筑物”的数量:

public static int nCases(String s)
{
    int ncases[] = new int[s.length()];
    for(int i = 0; i < s.length(); i++)
    {
        ncases[i] = 0;
        for(Map.Entry<Integer,Character> piece: library.entrySet())
        {
            String mapStr = piece.getKey().toString();
            int j = i+1-mapStr.length();
            int prev = 1;
            if(j-1>=0) prev = ncases[j-1];
            if(j>= 0 && s.substring(j, i+1).equals(mapStr))
                ncases[i] += prev;
        }
    }
    if(ncases.length>0)
        return ncases[ncases.length-1];
    return 0;
}

此解决方案可轻松修改以跟踪每个案例,从而提供您所要求的列表:

private static class Pair<T,L>
{

    public Pair(T first, L second) {
        this.first = first;
        this.second = second;
    }

    T first;
    L second;
}

public static  List<String> dynamicd(String s)
{
    Pair<Integer,List<String>> ncases[] = new Pair[s.length()];
    for(int i = 0; i < s.length(); i++)
    {
        ncases[i] = new Pair<Integer, List<String>>(0, new ArrayList<String>());
        for(Map.Entry<Integer,Character> piece: library.entrySet())
        {
            String mapStr = piece.getKey().toString();
            int j = i+1-mapStr.length();
            Pair<Integer, List<String>> prev = 
                new Pair<Integer, List<String>>(1,new ArrayList<String>());
            prev.second.add("");
            if(j-1>=0) prev = ncases[j-1];
            if(j>= 0 && s.substring(j, i+1).equals(mapStr))
            {
                ncases[i].first += prev.first;
                for(String pcase: prev.second)
                    ncases[i].second.add(pcase+piece.getValue());
            }
        }
    }
    if(ncases.length>0)
        return ncases[ncases.length-1].second;
    return new ArrayList<>();
}

如果N = s.length()M = library.size(),此方法的时间复杂度最差为O(N*M)

答案 2 :(得分:0)

这是代码(自我解释)

public static Collection<String> allPermutations(int num) {
    TreeNode root = createTree(toArray(num));
    Collection<String> result = new ArrayList<String>();
    allLeafNodes(root, result);
    return result;
}

private static Integer[] toArray(int num) {
    ArrayDeque<Integer> array = new ArrayDeque<Integer>();
    do{
        array.push(num % 10);
        num /= 10;
    } while  (num > 0);
    return array.toArray(new Integer[0]);
}

private static TreeNode createTree(Integer[] integers) {
    return doCreateTree(0, "", integers);
}

private static TreeNode doCreateTree(int index, String parentString, Integer[] integers) {
    String nodeData = parentString + AlphabetMatcher.match(index);
    TreeNode root = new TreeNode(nodeData);
    if (integers.length != 0) {

        root.left = doCreateTree(integers[0], nodeData, Arrays.copyOfRange(integers, 1, integers.length));

        if (integers.length > 1) {
            int newIndex = integers[0]* 10 + integers[1];
            root.right = doCreateTree(newIndex, nodeData, Arrays.copyOfRange(integers, 2, integers.length));
        }
    }

    return root;
}

private static void allLeafNodes(TreeNode root, Collection<String> result) {
    if (root != null) {
        if (root.right == null && root.left ==null) {
            result.add(root.data);
        }
        allLeafNodes(root.left, result);
        allLeafNodes(root.right, result);
    }

}

private static class TreeNode {
    private String data;
    private TreeNode left;
    private TreeNode right;

    public TreeNode(String data) {
        this.data = data;
    }
}

private static class AlphabetMatcher {
    private static final String[] alphabet = {"", "a", "b", "c", "d", "e",
            "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
            "s", "t", "u", "v", "w", "x", "v", "z"};
    public static String match(int number) {            
        return alphabet[number];
    }
}

以下是测试用例

@Test
public void allPermutationsTest() {
    Collection<String> result = StringUtil.allPermutations(1221);
    org.assertj.core.api.Assertions.assertThat(result).hasSize(5).containsAll(Arrays.asList("abba","abu", "ava", "lba", "lu"));

    result = StringUtil.allPermutations(10);
    org.assertj.core.api.Assertions.assertThat(result).hasSize(2).containsAll(Arrays.asList("a","j"));
}