Ответов (25)25
Есть несколько способов сделать это. Общие методы используют рекурсию, мемоизацию или динамическое программирование. Основная идея состоит в том, что вы создаете список всех строк длиной 1, а затем на каждой итерации для всех строк, созданных на последней итерации, добавляете эту строку, сцепленную с каждым символом в строке индивидуально. (индекс переменной в приведенном ниже коде отслеживает начало последней и следующей итерации)
Какой-то псевдокод:
list = originalString.split('')
index = (0,0)
list = [""]
for iteration n in 1 to y:
index = (index[1], len(list))
for string s in list.subset(index[0] to end):
for character c in originalString:
list.add(s + c)
тогда вам нужно будет удалить все строки длиной меньше x, они будут первыми (x-1) * len (originalString) записями в списке.
Хотя это не совсем ответ на ваш вопрос, вот один из способов сгенерировать каждую перестановку букв из ряда строк одинаковой длины: например, если вашими словами были «кофе», «joomla» и «moodle», вы можете ожидайте вывода типа "coodle", "joodee", "joffle" и т. д.
По сути, количество комбинаций - это (количество слов) в степени (количество букв в слове). Итак, выберите случайное число от 0 до количества комбинаций - 1, преобразуйте это число в основание (количество слов), затем используйте каждую цифру этого числа в качестве индикатора, из какого слова взять следующую букву.
например: в приведенном выше примере. 3 слова, 6 букв = 729 комбинаций. Выберите случайное число: 465. Преобразуйте в основание 3: 122020. Возьмите первую букву из слова 1, 2-ю из слова 2, 3-ю из слова 2, 4-ю из слова 0 ... и вы получите ... "joofle".
Если вам нужны все перестановки, просто выполните цикл от 0 до 728. Конечно, если вы просто выбираете одно случайное значение, гораздо более простым и менее запутанным способом будет перебирать буквы. Этот метод позволяет избежать рекурсии, если вам нужны все перестановки, плюс он заставляет вас выглядеть так, как будто вы знаете математику (tm) !
Если количество комбинаций слишком велико, вы можете разбить его на серию более мелких слов и соединить их в конце.
Лучше использовать поиск с возвратом
#include <stdio.h>
#include <string.h>
void swap(char *a, char *b) {
char temp;
temp = *a;
*a = *b;
*b = temp;
}
void print(char *a, int i, int n) {
int j;
if(i == n) {
printf("%s\n", a);
} else {
for(j = i; j <= n; j++) {
swap(a + i, a + j);
print(a, i + 1, n);
swap(a + i, a + j);
}
}
}
int main(void) {
char a[100];
gets(a);
print(a, 0, strlen(a) - 1);
return 0;
}
С # итеративно:
public List<string> Permutations(char[] chars)
{
List<string> words = new List<string>();
words.Add(chars[0].ToString());
for (int i = 1; i < chars.Length; ++i)
{
int currLen = words.Count;
for (int j = 0; j < currLen; ++j)
{
var w = words[j];
for (int k = 0; k <= w.Length; ++k)
{
var nstr = w.Insert(k, chars[i].ToString());
if (k == 0)
words[j] = nstr;
else
words.Add(nstr);
}
}
}
return words;
}
Рекурсивное решение на питоне. Преимущество этого кода в том, что он экспортирует словарь с ключами в виде строк и всеми возможными перестановками в виде значений. Включены все возможные длины строк, так что фактически вы создаете надмножество.
Если вам нужны только окончательные перестановки, вы можете удалить другие ключи из словаря.
В этом коде словарь перестановок является глобальным.
В базовом случае я сохраняю значение как обе возможности в списке. perms['ab'] = ['ab','ba']
.
Для большей длины строки функция относится к более низкой длине строки и включает ранее вычисленные перестановки.
Функция выполняет две функции:
- вызывает себя с меньшей строкой
- возвращает список перестановок конкретной строки, если она уже доступна. Если они возвращены самому себе, они будут использоваться для добавления к персонажу и создания новых перестановок.
Дорого по памяти.
perms = {}
def perm(input_string):
global perms
if input_string in perms:
return perms[input_string] # This will send a list of all permutations
elif len(input_string) == 2:
perms[input_string] = [input_string, input_string[-1] + input_string [-2]]
return perms[input_string]
else:
perms[input_string] = []
for index in range(0, len(input_string)):
new_string = input_string[0:index] + input_string[index +1:]
perm(new_string)
for entries in perms[new_string]:
perms[input_string].append(input_string[index] + entries)
return perms[input_string]
def gen( x,y,list): #to generate all strings inserting y at different positions
list = []
list.append( y+x )
for i in range( len(x) ):
list.append( func(x,0,i) + y + func(x,i+1,len(x)-1) )
return list
def func( x,i,j ): #returns x[i..j]
z = ''
for i in range(i,j+1):
z = z+x[i]
return z
def perm( x , length , list ): #perm function
if length == 1 : # base case
list.append( x[len(x)-1] )
return list
else:
lists = perm( x , length-1 ,list )
lists_temp = lists #temporarily storing the list
lists = []
for i in range( len(lists_temp) ) :
list_temp = gen(lists_temp[i],x[length-2],lists)
lists += list_temp
return lists
Рекурсивное решение с драйверным main()
методом.
public class AllPermutationsOfString {
public static void stringPermutations(String newstring, String remaining) {
if(remaining.length()==0)
System.out.println(newstring);
for(int i=0; i<remaining.length(); i++) {
String newRemaining = remaining.replaceFirst(remaining.charAt(i)+"", "");
stringPermutations(newstring+remaining.charAt(i), newRemaining);
}
}
public static void main(String[] args) {
String string = "abc";
AllPermutationsOfString.stringPermutations("", string);
}
}
Здесь есть много хороших ответов. Я также предлагаю очень простое рекурсивное решение на C++.
#include <string>
#include <iostream>
template<typename Consume>
void permutations(std::string s, Consume consume, std::size_t start = 0) {
if (start == s.length()) consume(s);
for (std::size_t i = start; i < s.length(); i++) {
std::swap(s[start], s[i]);
permutations(s, consume, start + 1);
}
}
int main(void) {
std::string s = "abcd";
permutations(s, [](std::string s) {
std::cout << s << std::endl;
});
}
Примечание : строки с повторяющимися символами не создают уникальных перестановок.
def permutation(str)
posibilities = []
str.split('').each do |char|
if posibilities.size == 0
posibilities[0] = char.downcase
posibilities[1] = char.upcase
else
posibilities_count = posibilities.length
posibilities = posibilities + posibilities
posibilities_count.times do |i|
posibilities[i] += char.downcase
posibilities[i+posibilities_count] += char.upcase
end
end
end
posibilities
end
Вот мой взгляд на нерекурсивную версию
код, написанный для языка java:
пакет namo.algorithms;
import java.util.Scanner;
public class Permuations {
public static int totalPermutationsCount = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("input string : ");
String inputString = sc.nextLine();
System.out.println("given input String ==> "+inputString+ " :: length is = "+inputString.length());
findPermuationsOfString(-1, inputString);
System.out.println("**************************************");
System.out.println("total permutation strings ==> "+totalPermutationsCount);
}
public static void findPermuationsOfString(int fixedIndex, String inputString) {
int currentIndex = fixedIndex +1;
for (int i = currentIndex; i < inputString.length(); i++) {
//swap elements and call the findPermuationsOfString()
char[] carr = inputString.toCharArray();
char tmp = carr[currentIndex];
carr[currentIndex] = carr[i];
carr[i] = tmp;
inputString = new String(carr);
//System.out.println("chat At : current String ==> "+inputString.charAt(currentIndex));
if(currentIndex == inputString.length()-1) {
totalPermutationsCount++;
System.out.println("permuation string ==> "+inputString);
} else {
//System.out.println("in else block>>>>");
findPermuationsOfString(currentIndex, inputString);
char[] rarr = inputString.toCharArray();
char rtmp = carr[i];
carr[i] = carr[currentIndex];
carr[currentIndex] = rtmp;
inputString = new String(carr);
}
}
}
}
Возможные перестановки строк можно вычислить с помощью рекурсивной функции. Ниже приводится одно из возможных решений.
public static String insertCharAt(String s, int index, char c) {
StringBuffer sb = new StringBuffer(s);
StringBuffer sbb = sb.insert(index, c);
return sbb.toString();
}
public static ArrayList<String> getPerm(String s, int index) {
ArrayList<String> perm = new ArrayList<String>();
if (index == s.length()-1) {
perm.add(String.valueOf(s.charAt(index)));
return perm;
}
ArrayList<String> p = getPerm(s, index+1);
char c = s.charAt(index);
for(String pp : p) {
for (int idx=0; idx<pp.length()+1; idx++) {
String ss = insertCharAt(pp, idx, c);
perm.add(ss);
}
}
return perm;
}
public static void testGetPerm(String s) {
ArrayList<String> perm = getPerm(s,0);
System.out.println(s+" --> total permutation are :: "+perm.size());
System.out.println(perm.toString());
}
Рекурсивный подход
func StringPermutations(inputStr string) (permutations []string) {
for i := 0; i < len(inputStr); i++ {
inputStr = inputStr[1:] + inputStr[0:1]
if len(inputStr) <= 2 {
permutations = append(permutations, inputStr)
continue
}
leftPermutations := StringPermutations(inputStr[0 : len(inputStr)-1])
for _, leftPermutation := range leftPermutations {
permutations = append(permutations, leftPermutation+inputStr[len(inputStr)-1:])
}
}
return
}
У вас будет много струн, это точно ...
Где x и y - это то, как вы их определяете, а r - количество символов, из которых мы выбираем, - если я правильно вас понимаю. Вы обязательно должны генерировать их по мере необходимости, а не быть небрежным и, скажем, сгенерировать набор мощности, а затем отфильтровать длину строк.
Следующее определенно не лучший способ их создания, но тем не менее интересно.
Кнут (том 4, глава 2, 7.2.1.3) говорит нам, что (s, t) -комбинация эквивалентна s + 1 вещей, взятых t за раз с повторением - (s, t) -комбинация используется Кнут, что равно . Мы можем выяснить это, сначала сгенерировав каждую (s, t) -комбинацию в двоичной форме (т.е. длины (s + t)) и посчитав количество нулей слева от каждой единицы.
10001000011101 -> становится перестановкой: {0, 3, 4, 4, 4, 1}
Некоторый рабочий код Java, основанный на ответе Сарпа :
public class permute {
static void permute(int level, String permuted,
boolean used[], String original) {
int length = original.length();
if (level == length) {
System.out.println(permuted);
} else {
for (int i = 0; i < length; i++) {
if (!used[i]) {
used[i] = true;
permute(level + 1, permuted + original.charAt(i),
used, original);
used[i] = false;
}
}
}
}
public static void main(String[] args) {
String s = "hello";
boolean used[] = {false, false, false, false, false};
permute(0, "", used, s);
}
}
Я просто быстро придумал это в Ruby:
def perms(x, y, possible_characters)
all = [""]
current_array = all.clone
1.upto(y) { |iteration|
next_array = []
current_array.each { |string|
possible_characters.each { |c|
value = string + c
next_array.insert next_array.length, value
all.insert all.length, value
}
}
current_array = next_array
}
all.delete_if { |string| string.length < x }
end
Вы можете изучить языковой API для встроенных функций типа перестановки, и вы, возможно, сможете написать более оптимизированный код, но если числа настолько высоки, я не уверен, что есть много способов обойтись без большого количества результатов. .
В любом случае, идея кода заключается в том, чтобы начать со строки длины 0, а затем отслеживать все строки длины Z, где Z - текущий размер в итерации. Затем просмотрите каждую строку и добавьте каждый символ в каждую строку. Наконец, в конце удалите все, что было ниже порога x, и верните результат.
Я не тестировал его с потенциально бессмысленным вводом (список нулевых символов, странные значения x и y и т. Д.).
Это перевод Ruby-версии Майка на Common Lisp:
(defun perms (x y original-string)
(loop with all = (list "")
with current-array = (list "")
for iteration from 1 to y
do (loop with next-array = nil
for string in current-array
do (loop for c across original-string
for value = (concatenate 'string string (string c))
do (push value next-array)
(push value all))
(setf current-array (reverse next-array)))
finally (return (nreverse (delete-if #'(lambda (el) (< (length el) x)) all)))))
И еще одна версия, немного короче и использующая больше функций петли:
(defun perms (x y original-string)
(loop repeat y
collect (loop for string in (or (car (last sets)) (list ""))
append (loop for c across original-string
collect (concatenate 'string string (string c)))) into sets
finally (return (loop for set in sets
append (loop for el in set when (>= (length el) x) collect el)))))
Рекурсивное решение в C++
int main (int argc, char * const argv[]) {
string s = "sarp";
bool used [4];
permute(0, "", used, s);
}
void permute(int level, string permuted, bool used [], string &original) {
int length = original.length();
if(level == length) { // permutation complete, display
cout << permuted << endl;
} else {
for(int i=0; i<length; i++) { // try to add an unused character
if(!used[i]) {
used[i] = true;
permute(level+1, original[i] + permuted, used, original); // find the permutations starting with this string
used[i] = false;
}
}
}
Вот простое слово C# рекурсивное решение:
Метод:
public ArrayList CalculateWordPermutations(string[] letters, ArrayList words, int index)
{
bool finished = true;
ArrayList newWords = new ArrayList();
if (words.Count == 0)
{
foreach (string letter in letters)
{
words.Add(letter);
}
}
for(int j=index; j<words.Count; j++)
{
string word = (string)words[j];
for(int i =0; i<letters.Length; i++)
{
if(!word.Contains(letters[i]))
{
finished = false;
string newWord = (string)word.Clone();
newWord += letters[i];
newWords.Add(newWord);
}
}
}
foreach (string newWord in newWords)
{
words.Add(newWord);
}
if(finished == false)
{
CalculateWordPermutations(letters, words, words.Count - newWords.Count);
}
return words;
}
Звонок:
string[] letters = new string[]{"a","b","c"};
ArrayList words = CalculateWordPermutations(letters, new ArrayList(), 0);
Вы можете посмотреть « Эффективное перечисление подмножеств набора », в котором описывается алгоритм, позволяющий выполнить часть того, что вы хотите - быстро сгенерировать все подмножества из N символов от длины x до y. Он содержит реализацию на C.
Для каждого подмножества вам все равно придется сгенерировать все перестановки. Например, если вам нужно 3 символа из «abcde», этот алгоритм даст вам «abc», «abd», «abe» ... но вам придется переставлять каждый из них, чтобы получить «acb», «bac», «BCA» и др.
Вот простое решение на C#.
Он генерирует только различные перестановки данной строки.
static public IEnumerable<string> permute(string word)
{
if (word.Length > 1)
{
char character = word[0];
foreach (string subPermute in permute(word.Substring(1)))
{
for (int index = 0; index <= subPermute.Length; index++)
{
string pre = subPermute.Substring(0, index);
string post = subPermute.Substring(index);
if (post.Contains(character))
continue;
yield return pre + character + post;
}
}
}
else
{
yield return word;
}
}
Нерекурсивное решение по примеру Knuth, Python:
def nextPermutation(perm):
k0 = None
for i in range(len(perm)-1):
if perm[i]<perm[i+1]:
k0=i
if k0 == None:
return None
l0 = k0+1
for i in range(k0+1, len(perm)):
if perm[k0] < perm[i]:
l0 = i
perm[k0], perm[l0] = perm[l0], perm[k0]
perm[k0+1:] = reversed(perm[k0+1:])
return perm
perm=list("12345")
while perm:
print perm
perm = nextPermutation(perm)
permute (ABC) -> A.perm (BC) -> A.perm [B.perm (C)] -> A.perm [( * B C), (C B * )] -> [( * A BC ), (B A C), (BC A * ), ( * A CB), (C A B), (CB A * )] Чтобы удалить дубликаты при вставке каждого алфавита, проверьте, заканчивается ли предыдущая строка тем же алфавитом (почему? -упражнение)
public static void main(String[] args) {
for (String str : permStr("ABBB")){
System.out.println(str);
}
}
static Vector<String> permStr(String str){
if (str.length() == 1){
Vector<String> ret = new Vector<String>();
ret.add(str);
return ret;
}
char start = str.charAt(0);
Vector<String> endStrs = permStr(str.substring(1));
Vector<String> newEndStrs = new Vector<String>();
for (String endStr : endStrs){
for (int j = 0; j <= endStr.length(); j++){
if (endStr.substring(0, j).endsWith(String.valueOf(start)))
break;
newEndStrs.add(endStr.substring(0, j) + String.valueOf(start) + endStr.substring(j));
}
}
return newEndStrs;
}
Печатает все перестановки без дубликатов
Вот элегантное, нерекурсивное решение O (n!):
public static StringBuilder[] permutations(String s) {
if (s.length() == 0)
return null;
int length = fact(s.length());
StringBuilder[] sb = new StringBuilder[length];
for (int i = 0; i < length; i++) {
sb[i] = new StringBuilder();
}
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
int times = length / (i + 1);
for (int j = 0; j < times; j++) {
for (int k = 0; k < length / times; k++) {
sb[j * length / times + k].insert(k, ch);
}
}
}
return sb;
}