【问题标题】:Find shortest text containing all combinations of a given string查找包含给定字符串的所有组合的最短文本
【发布时间】:2014-09-24 04:10:02
【问题描述】:

给定一个字符串,找到另一个包含输入字符串所有组合的字符串。

示例

如果输入字符串 = "23",那么它的组合将是 ["22", "23", "32", "33"],

包含上述所有组合的字符串之一是“22233233”,但这不是最短的。最短的是“22332”。

算法应该足够通用以适用于任何大小的输入字符串。 (假设输入不是太大,输出将保持在正常的 int/string/jvm 等大小。还假设输入字符串将仅包含来自英文的字母数字字符)

我尝试了以下算法,但它似乎不起作用:

1) 查找字符串 = ["22", "23", "32", "33"]的所有组合

2) 构建前缀映射 [2: {22, 23}, 3: {32, 33}]

3) 从前缀映射中的任意组合和查找后缀开始。

示例:以 22 开头,后缀为 2 从前缀映射中,对应 2 的值是 22 和 23。 在这里选择一个不是当前选择的单词,所以它会给出 23

4) 将选中的单词的后缀添加到当前字符串(这给出 223)

5) 重复。 所以我会得到 223 的后缀 = 3 从前缀映射,3: {32, 33} 选择任何一个,比如 32 追加到当前字符串得到 2232

6) 如果没有其他匹配项,则追加到当前字符串。这给出了 223233

但是,答案应该是 22332,因为它是最短的。

这是我到目前为止编写的完整代码:

public class TextContainingAllPermutations
{
    static String input = "ABC";

    public static void main (String args[])
    {
        int suffixLen = input.length()-1;
        Set<String> combinations = getCombinations();
        while (suffixLen > 0 && combinations.size() > 1)
        {
            Map<String, List<String>> suffixToWords = getPrefixMap(combinations, suffixLen);
            String someWordsString = combinations.iterator().next();
            combinations.remove(someWordsString);
            Set<String> combinations2 = new HashSet<String>();

            while (combinations.size() > 0)
            {
                String suffix = someWordsString.substring(someWordsString.length()-suffixLen);
                List<String> words = suffixToWords.get(suffix);
                if (words == null || words.size()==0)
                {
                    combinations2.add(someWordsString);
                    System.out.println (someWordsString);
                    if (combinations.size() == 0)
                        break;
                    someWordsString = combinations.iterator().next();
                    combinations.remove(someWordsString);
                }
                else
                {
                    String w = words.get(words.size()-1);
                    words.remove(words.size()-1);
                    combinations.remove(w);
                    if (someWordsString.indexOf(w) == -1)
                        someWordsString += w.charAt(w.length()-1); // append last char
                }
            }
            combinations2.add(someWordsString);
            System.out.println (someWordsString);
            combinations = combinations2;
            suffixLen--;
        }
    }

    private static Map<String, List<String>> getPrefixMap(Set<String> combinations, int suffixLen)
    {
        Map<String, List<String>> suffixToWords = new HashMap<String, List<String>>();
        for (String s: combinations)
        {
            String suffix = s.substring(0,suffixLen);
            if (!suffixToWords.containsKey(suffix))
            {
                suffixToWords.put(suffix, new ArrayList<String>());
            }
            suffixToWords.get(suffix).add(s);
        }
        return suffixToWords;
    }

    static Set<String> getCombinations()
    {
        char[] inputChars = input.toCharArray();
        int N = (int)Math.pow(input.length(), input.length());
        Set<String> combinations = new HashSet<String>(N);
        for (int i=0; i<N; i++)
        {
            char[] binary = padZeroes(Integer.toString(i, input.length())).toCharArray();

            String combination = "";
            for (int j=0; j<inputChars.length; j++)
            {
                char c = binary[j];
                int index = c - '0';
                char inputChar = inputChars[index];
                combination = inputChar + combination;
            }

            System.out.println (new String(binary) + " = " + combination);
            combinations.add(combination);
        }
        return combinations;
    }

    private static String padZeroes(String s)
    {
        int j = input.length()-s.length();
        for (int i=0; i<j; i++)
            s = '0' + s;
        return s;
    }
}

这不是作业问题。

【问题讨论】:

  • 你确定什么会起作用或不会起作用?
  • 这个问题似乎是题外话,因为它是一个“给我代码”的问题。你可能应该Take the Tour
  • 这可能不是家庭作业问题,我愿意为您提供怀疑的好处但是......您没有任何努力的迹象,所以很难说服我们否则。
  • @Makoto,我在这里更新了我的算法。我也可以把完整的代码放在这里来展示我的尝试。
  • 这肯定会帮助您的事业。提及为什么您认为它不起作用也会有所帮助,因为“它不起作用”并不是对问题的一个很好的描述。尽管我很不愿意承认,但我在工作中使用过一两次,但在问题中添加更多信息通常会得到令人满意的答案。

标签: java algorithm


【解决方案1】:

您要查找的基本上是De Bruijn sequence。 De Bruijn 序列B(k,n) 是一个循环序列,它包含来自一组k 符号的所有可能长度为n 的子序列,每个符号只出现一次。序列的长度正好是k<sup>n</sup>

可以通过在任意点打破循环,然后将第一个n-1符号复制到末尾,得到一个长度为k<sup>n</sup> + n - 1的序列,这显然是最小的。

有多种技术可以生成 De Bruijn 序列。最简单的描述技术是,de Bruijn 序列由所有Lyndon words 在长度除以n 的字母表上的串联组成,按字典顺序排列。 (Lyndon 词是一个序列,它在字典上在其自身的任何旋转之前。这意味着 Lyndon 词是非周期性的。)

有一种简单的算法可以在字母表上按字典顺序生成最大长度 n 的 Lyndon 词:

  1. 从仅包含集合中按字典顺序排列的第一个字符的长度开始。
  2. 尽可能长的时间,通过循环重复前一个单词直到长度n(如有必要,丢弃上次重复中的额外符号)形成下一个单词,然后通过以下方式“增加”单词:
    1. 只要单词中的最后一个符号是字母表中字典序上最大的符号,就将其删除。
    2. 如果单词中仍有符号,请将最后一个符号更改为字典序。如果单词中没有符号,则制作完成。

为了生成顺序为n 的De Bruijn 序列,我们生成了上述Lyndon 词的序列,但我们只保留长度除以n 的词。由于“几乎所有”最大长度的 Lyndon 词 n 实际上的长度为 n,因此该算法可以被认为是每个符号 O(1),或者对于完整序列来说是 O(k<sup>n</sup>)

在问题要求的具体情况下,k == n

【讨论】:

    猜你喜欢
    • 2013-05-13
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    • 2012-05-04
    相关资源
    最近更新 更多