Создание строк с предопределенными условиями

Есть ли в Java что-нибудь, противоположное регулярным выражениям?

Моя задача: учитывая определенную общую длину строки и каждая позиция может состоять только из предопределенных определенных символов, сгенерировать все возможные строки.

Приведу пример: я хочу создать все строки длиной 3, где позиции определяются как

[ABC][123][XYZ]

Это означает, что первая позиция может быть только A, B or C, вторая позиция — одно из чисел 1 to 3 и так далее. Таким образом, допустимые строки будут

A1X 
A1Y 
A1Z 
A2X 
A2Y 
A2Z 
...
... 
C3Z 

Для длины три я, конечно, могу использовать вложенный цикл. Моя проблема в том, что я заранее не знаю, какой длины должна быть строка или сколько допустимых символов имеет каждая позиция. Есть идеи?

Код длиной 3 и 3 возможных символа в каждой позиции:

public static void main(String[] args) {
    String[] first  = {"A", "B", "C"};
    String[] second = {"1", "2", "3"};
    String[] third  = {"X", "Y", "Z"};

    List<String> result = createStrings(first, second, third);

    result.forEach(System.out::println);
}

static List<String> createStrings(String[] ... strs) {
    String[] first  = strs[0];
    String[] second = strs[1];
    String[] third  = strs[2];

    List<String> result = new ArrayList<>();
    for (int i = 0; i < first.length; i++) {
        for (int j = 0; j < second.length; j++) {
            for (int k = 0; k < third.length; k++) {
                result.add(first[i] + second[j] + third[k]);
            }
        }
    }
    return result;
}

Мне нужно что-то гибкое, работающее для всех входов. Или способ динамического создания вложенного цикла в зависимости от strs.length, который определяет, сколько циклов мне нужно.

«Есть ли в Java что-нибудь, что действует противоположно регулярным выражениям?» возможно, это связано: Как мне создать текст, соответствующий регулярному выражению, из регулярного выражения?

Pshemo 07.07.2024 22:21

Вы спрашиваете, как сгенерировать декартово произведение массивов, содержащих строки? Если да, см. Декартово произведение произвольного числа множеств, датируемое 2009 годом.

Cary Swoveland 08.07.2024 07:35
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
2
96
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать рекурсию:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        String[] first = { "A", "B", "C" };
        String[] second = { "1", "2", "3" };
        String[] third = { "X", "Y", "Z" };
        String[] fourth = { "K", "L", "M" };
        String[] fifth = { "7", "8", "9" };

        List<String> result = createStrings(first, second, third, fourth, fifth);

        result.forEach(System.out::println);
    }

    static List<String> createStrings(String[]... strs) {
        List<String> res = new ArrayList<>();
        getStrings(0, "", res, strs);
        return res;
    }

    static void getStrings(int level, String curr, List<String> res, String[]... strs) {
        if (level == strs.length) {
            res.add(curr);
            return;
        }

        for (String ch : strs[level]) {
            getStrings(level + 1, curr + ch, res, strs);
        }
    }
}

Принты

A1XK7
A1XK8
A1XK9
A1XL7
A1XL8
A1XL9
A1XM7
...

C3ZK9
C3ZL7
C3ZL8
C3ZL9
C3ZM7
C3ZM8
C3ZM9

Построение строки на уровне дерева:


                              ""
                            /  |  \
                          A    B    C
                         /|\  /|\  /|\
                       1 2 3 1 2 3 1 2 3
                      /|\ /|\ /|\ /|\ /|\
                     X Y Z X Y Z X Y Z X Y Z
                    /|/|/|/|/|/|/|/|\
                   K L M K L M K L M K L M K L M
                  /|/|/|/|/|/|/|/|/|/|\
                 ... ... ... ... ... ... ... ... 
  • В этом примере у нас есть пять уровней. Мы хотим сгенерировать все возможные комбинации символов путем рекурсивного объединения каждого символа (с каждого уровня) с использованием текущего массива (strs[level]), а затем перейти на следующий уровень.

  • Первоначально мы вызываем createStrings() со всеми пятью массивами, что вызывает getStrings(0, "", res, strs).

Вот стеки рекурсии:

Первый уровень (уровень = 0):

  • Вызовы с curr="A", curr="B", curr="C"

Второй уровень (уровень = 1):

  • Для curr="A": Вызовы с curr="A1", curr="A2", curr="A3"
  • Для curr="B": Вызовы с curr="B1", curr="B2", curr="B3"
  • Для curr="C": Вызовы с curr="C1", curr="C2", curr="C3"

Третий уровень (уровень = 2):

  • Для curr="A1": Вызовы с curr="A1X", curr="A1Y", curr="A1Z"
  • Для curr="A2": Вызовы с curr="A2X", curr="A2Y", curr="A2Z"
  • Для curr="A3": Вызовы с curr="A3X", curr="A3Y", curr="A3Z"
  • ...

Четвертый уровень (уровень = 3):

  • Для curr="A1X": Вызовы с curr="A1XK", curr="A1XL", curr="A1XM"
  • Для curr="A1Y": Вызовы с curr="A1YK", curr="A1YL", curr="A1YM"
  • Для curr="A1Z": Вызовы с curr="A1ZK", curr="A1ZL", curr="A1ZM"
  • ...

Пятый уровень (уровень = 4):

  • Для curr="A1XK": Вызовы с curr="A1XK7", curr="A1XK8", curr="A1XK9"
  • Для curr="A1XL": Вызовы с curr="A1XL7", curr="A1XL8", curr="A1XL9"
  • Для curr = "A1XM": вызовы с curr = "A1XM7", curr = "A1XM8", curr = "A1XM9"

...


Давайте проследим один путь через стек рекурсии:

  • Первый звонок: getStrings(0, "", res, strs), звонки getStrings(1, "A", res, strs);
  • Второй звонок: getStrings(1, "A", res, strs), звонки getStrings(2, "A1", res, strs);
  • Третий звонок: getStrings(2, "A1", res, strs), звонки getStrings(3, "A1X", res, strs);
  • Четвертый звонок: getStrings(3, "A1X", res, strs), звонки getStrings(4, "A1XK", res, strs);
  • Пятый звонок: getStrings(4, "A1XK", res, strs), звонки getStrings(5, "A1XK7", res, strs); и
  • Базовый вариант: getStrings(5, "A1XK7", res, strs), к res добавляется «A1XK7».

Большое спасибо. это действительно работает для всех случаев, которые я тестировал на месте. Мне просто трудно понять рекурсию. можешь ли ты написать об этом пару предложений? В частности, что именно здесь происходит: getStrings(level + 1, curr + ch, res, strs);и как/где находится условие завершения рекурсии?

wannaBeDev 07.07.2024 22:03

Ух ты, лучший ответ, который я когда-либо получал на stackoverflow. еще раз спасибо. Жаль, что я могу проголосовать за это только один раз.

wannaBeDev 07.07.2024 22:28

Один из способов составить список всех комбинаций нескольких массивов символов — создать счетчик, который подсчитывает возможные варианты.

Код создает int массив цифр и int массив пределов. Мы считаем массив цифр до тех пор, пока не будет достигнут предел в каждой позиции. Затем цифра сбрасывается на ноль и следующая цифра увеличивается.

Вот так (при условии, что каждый массив символов имеет три возможности):

000
001
002
010
011
...

Вот полный работоспособный код.

import java.util.ArrayList;
import java.util.List;

public class CreateStrings {

    public static void main(String[] args) {
        String[] first = { "A", "B", "C" };
        String[] second = { "1", "2", "3", "4" };
        String[] third = { "X", "Y", "Z" };

        List<String> result = createStrings(first, second, third);
        result.forEach(System.out::println);
    }

    private static List<String> createStrings(String[]... strs) {
        List<String> strings = new ArrayList<>();
        int[] digits = new int[strs.length];
        int[] limits = new int[strs.length];
        for (int index = 0; index < strs.length; index++) {
            limits[index] = strs[index].length;
        }

        boolean inProcess = true;
        while (inProcess) {
            String s = "";
            for (int index = 0; index < digits.length; index++) {
                s += strs[index][digits[index]];
            }
//          System.out.println(s);
            strings.add(s);

            for (int index = digits.length - 1; index >= 0; index--) {
                digits[index]++;
                if (digits[index] >= limits[index]) {
                    if (index == 0) {
                        inProcess = false;
                    }
                    digits[index] = 0;
                } else {
                    break;
                }
            }
        }

        return strings;
    }

}

Также приятно увидеть итеративный вариант, который я обязательно рассмотрю поближе. но я должен сказать, что рекурсивный вариант кажется мне несколько более элегантным. Тем не менее, большое спасибо и вам.

wannaBeDev 07.07.2024 22:36

Другие вопросы по теме