Вы можете попробовать с помощью рекурсивного подхода. Основная идея - проверить, что каждое слово - это другие слова, которые могут быть в цепочке.
private String[] findRing(String... words) {
for (int i = 0; i < words.length; i++) {
String word = words[i];
String[] result = findRingForHeadAndTail(word, dropElementWithIndex(i, words));
if ((result.length == words.length - 1) &&
(word.charAt(0) == result[result.length - 1].charAt(result[result.length - 1].length() - 1))) { //word started from last letter in tail
return concatHeadAndTail(word, result);
}
}
return new String[]{"NO SOLUTION"};
}
private String[] findRingForHeadAndTail(
String head, //first word
String... tail //other words
) {
if (tail.length == 0) { // if we don't have other words then just return empty array
return new String[0];
}
if (tail.length == 1) { // if we have just one word in tail
if (tail[0].charAt(0) == head.charAt(head.length() - 1)) { //and this word begins with last letter in head
return tail; //return this tail
} else {
return new String[0]; //otherwise return empty array
}
}
for (int i = 0; i < tail.length; i++) { // for every word
String word = tail[i];
if (word.charAt(0) == head.charAt(head.length() - 1)) { // if this word begins with last letter in head
String[] result = findRingForHeadAndTail(//recursive call for
word, //picked word
dropElementWithIndex(i, tail) // all other words in tail
);
if (result.length == tail.length - 1) { // if recursive call returns the same amount of words as we passed there as tail
return concatHeadAndTail(word, result); //return array {head, tail}
}
}
}
return new String[0];
}
//returns array as {head, tail}
private String[] concatHeadAndTail(String head, String... tail) {
return Stream.concat(Stream.of(head), Stream.of(tail)).collect(Collectors.toList()).toArray(new String[tail.length + 1]);
}
//removes element with index i from words
private String[] dropElementWithIndex(int i, String... words) {
List<String> result = new ArrayList<>();
for (int j = 0; j < words.length; j++) {
if (j != i) {
result.add(words[j]);
}
}
return result.toArray(new String[words.length - 1]);
}
И выход для:
@Test
public void findRing() {
System.out.println(Arrays.toString(findRing("squirrel", "eyes", "klondlike", "lasik")));
System.out.println(Arrays.toString(findRing("why", "cant", "we", "dance")));
System.out.println(Arrays.toString(findRing("apple", "tampa", "elephant")));
System.out.println(Arrays.toString(findRing("apple", "elephant", "tampa")));
System.out.println(Arrays.toString(findRing("elephant", "apple", "tampa")));
System.out.println(Arrays.toString(findRing("alfa", "alfa", "beta")));
System.out.println(Arrays.toString(findRing("alfa", "alfa")));
System.out.println(Arrays.toString(findRing("alfa", "alfa", "bravo")));
}
является:
[squirrel, lasik, klondlike, eyes]
[NO SOLUTION]
[apple, elephant, tampa]
[apple, elephant, tampa]
[elephant, tampa, apple]
[NO SOLUTION]
[alfa, alfa]
[NO SOLUTION]
UPDATE Предыдущее решение имеет сложность O (N!) В худшем случае. Различные решения были предложены FDesu и основаны на одном из свойств Adjacency matrix.
Если А матрица смежности направленного или ненаправленного графа G, то матрица Ап (т.е. матрица произведение п экземпляров А) имеет интересную интерпретацию: элемент (I, J) дает количество (направленных или неориентированных) прогулок длины n от вершины i до вершины j.
Этот алгоритм имеет сложность O (n^4), но также имеет дополнительные требования к памяти, поскольку мы должны хранить все возможные пути.
private void findRingWithMatrix(String... words) {
System.out.println("For example: " + Arrays.toString(words));
boolean[][] initialAdjacencyMatrix = new boolean[words.length][words.length];
List<String[]> paths = new ArrayList<>();
//Build initial adjacency matrix
for (int i = 0; i < words.length; i++) {
for (int j = 0; j < words.length; j++) {
initialAdjacencyMatrix[i][j] = (i != j) && (couldBeInChain(words[i], words[j]));
//if node is reachable
if (initialAdjacencyMatrix[i][j]) {
//put this path to possible paths
paths.add(new String[]{words[i], words[j]});
}
}
}
//create temporary copy of matrix to multiply
boolean[][] tempAdjacencyMatrix = initialAdjacencyMatrix.clone();
//We should multiply matrix N-1 times, because first step in graph we've already done on previous step
for (int n = 1; n < words.length; n++) {
boolean[][] bufferAdjacencyMatrix = new boolean[words.length][words.length];
List<String[]> newPathes = new ArrayList<>();
//multiply matrices (next step and initial). In result we get [true] in node which is reachable from first node in N steps
for (int i = 0; i < words.length; i++) {
for (int j = 0; j < words.length; j++) {
for (int k = 0; k < words.length; k++) {
bufferAdjacencyMatrix[i][j] |= (tempAdjacencyMatrix[i][k] && initialAdjacencyMatrix[k][j]);
}
//if node reachable
if (bufferAdjacencyMatrix[i][j]) {
//create new path and put it list of possible paths
for (String[] path : paths) {
if (couldBeInChain(path[path.length - 1], words[j])) {
String[] newPath = new String[path.length + 1];
System.arraycopy(path, 0, newPath, 0, path.length);
newPath[newPath.length - 1] = words[j];
newPathes.add(newPath);
}
}
}
}
}
paths = removeDuplicates(newPathes);
System.out.println("Step #" + n);
printMatrix(bufferAdjacencyMatrix);
tempAdjacencyMatrix = bufferAdjacencyMatrix;
}
boolean isRing = true;
//Ring could be just if after (N-1) multiplications we have [true] in main diagonal.
//In other words, can reach every node in N steps.
for (int i = 0; i < words.length; i++) {
isRing = tempAdjacencyMatrix[i][i];
if (!isRing) {
break;
}
}
if (!isRing) {
System.out.println("NO SOLUTION");
return;
} else {
System.out.println("Found solutions:");
for (String[] path : paths) {
//we are interested just in solutions when first node is equals to last one
if (path[0].equals(path[path.length - 1])) {
String[] toPrint = new String[path.length - 1];
System.arraycopy(path, 0, toPrint, 0, toPrint.length);
System.out.println(Arrays.deepToString(toPrint));
}
}
}
System.out.println("==============================");
}
private boolean couldBeInChain(String first, String second) {
return first.charAt(first.length() - 1) == second.charAt(0);
}
private List<String[]> removeDuplicates(List<String[]> newPathes) {
return newPathes
.stream()
.map(Arrays::asList)
.collect(Collectors.collectingAndThen(Collectors.toSet(), new Function<Set<List<String>>, List<String[]>>() {
@Override
public List<String[]> apply(Set<List<String>> lists) {
List<String[]> result = new ArrayList<>();
for (List<String> list : lists) {
result.add(list.toArray(new String[list.size()]));
}
return result;
}
}));
}
private void printMatrix(boolean[][] matrix) {
System.out.println("-------------------------------");
for (boolean[] row : matrix) {
System.out.println(Arrays.toString(row));
}
System.out.println("-------------------------------");
}
Я думаю * возможно * Я получаю то, что вы пытаетесь сделать, но, чтобы быть уверенным, можете ли вы отредактировать свой вопрос, чтобы включить некоторые примеры входов/выходов? –
жаль, что я его отредактировал – myt
Вы пробовали переходить через свой код с помощью отладчика? –