Есть ли в 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
, который определяет, сколько циклов мне нужно.
Вы спрашиваете, как сгенерировать декартово произведение массивов, содержащих строки? Если да, см. Декартово произведение произвольного числа множеств, датируемое 2009 годом.
Вы можете использовать рекурсию:
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)
.
Вот стеки рекурсии:
...
Давайте проследим один путь через стек рекурсии:
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);
и как/где находится условие завершения рекурсии?
Ух ты, лучший ответ, который я когда-либо получал на stackoverflow. еще раз спасибо. Жаль, что я могу проголосовать за это только один раз.
Один из способов составить список всех комбинаций нескольких массивов символов — создать счетчик, который подсчитывает возможные варианты.
Код создает 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;
}
}
Также приятно увидеть итеративный вариант, который я обязательно рассмотрю поближе. но я должен сказать, что рекурсивный вариант кажется мне несколько более элегантным. Тем не менее, большое спасибо и вам.
«Есть ли в Java что-нибудь, что действует противоположно регулярным выражениям?» возможно, это связано: Как мне создать текст, соответствующий регулярному выражению, из регулярного выражения?