查找包含给定字符串的所有组合的最短文本

时间:2014-09-24 04:10:03

标签: java algorithm

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

示例

如果输入字符串=“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;
    }
}

这不是一个家庭作业问题。

1 个答案:

答案 0 :(得分:5)

您所寻找的基本上是De Bruijn sequence。 De Bruijn序列B(k,n)是一个循环序列,其中包含来自一组n符号的长度k的所有可能子序列,每个符号恰好出现一次。序列的长度恰好是kn

可以通过在任何点断开循环然后将第一个n-1符号复制到末尾来获得最小非循环序列,从而产生长度为kn + n - 1的序列,这显然是最小的。

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

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

  1. 从仅包含字典中第一个字符的长度开始。
  2. 只要有可能,通过循环重复上一个单词长度n来形成下一个单词(如果需要,丢弃上次重复的额外符号),然后&#34;递增&#34;这个词由:
    1. 只要单词中的最后一个符号是字母表中按字典顺序排列的最大符号,就将其删除。
    2. 如果单词中仍有符号,请将最后一个符号更改为词典后继符号。如果单词中没有符号,则表示制作完成。
  3. 为了使De Bruijn序列为n,我们生成上述Lyndon单词序列,但我们只保留长度除以n的序列。因为&#34;几乎所有&#34; Lyndon单词的最大长度n实际上是长度n,算法可以被认为是每个符号为O(1),或者O(kn)为完整序列。

    在问题所要求的特定情况下,k == n