给定一个字符串,找到另一个包含输入字符串的所有组合的字符串。
示例:
如果输入字符串=“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;
}
}
这不是一个家庭作业问题。
答案 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单词:
n
来形成下一个单词(如果需要,丢弃上次重复的额外符号),然后&#34;递增&#34;这个词由:
为了使De Bruijn序列为n
,我们生成上述Lyndon单词序列,但我们只保留长度除以n
的序列。因为&#34;几乎所有&#34; Lyndon单词的最大长度n
实际上是长度n
,算法可以被认为是每个符号为O(1),或者O(kn)
为完整序列。
在问题所要求的特定情况下,k == n
。