Can someone help to get Shortest Path with Obstacles? - java

I have a 2D matrix.
Given a 2D matrix where some of the elements are filled with '1' and the rest of the elements are filled with '0', except 2 elements, of which one is S (start point) and D (endpoint). Here '0' means you cannot traverse to that particular point. From a cell you can either traverse to left, right, up or down. Given two points in the matrix find the shortest path between these points.
One of the shortest paths (from S to D both exclusive) is: [(3, 2), (3, 1), (2, 1), (2, 0)]. Return null if there is no path between S and D.
I have writtes a piece of code which returns the distance to reach from S to D, my method returns int but how to return as expected output?
My code:
public class ShortestPath {
public static void main(String args[])
{
char[][] matrix = {
{'S', '0', '1', '1'},
{'1', '1', '0', '1'},
{'0', '1', '1', '1'},
{'1', '0', 'D', '1'}
};
int path = pathExists(matrix);
System.out.println(path);
}
private static int pathExists(char[][] matrix) {
Node source = new Node(0, 0, 0);
Queue<Node> queue = new LinkedList<Node>();
queue.add(source);
while(!queue.isEmpty()) {
Node poped = queue.poll();
if(matrix[poped.x][poped.y] == 'D' ) {
return poped.distanceFromSource;
}
else {
matrix[poped.x][poped.y]='0';
List<Node> neighbourList = addNeighbours(poped, matrix);
queue.addAll(neighbourList);
}
}
return -1;
}
private static List<Node> addNeighbours(Node poped, char[][] matrix) {
List<Node> list = new LinkedList<Node>();
if((poped.x-1 > 0 && poped.x-1 < matrix.length) && matrix[poped.x-1][poped.y] != '0') {
list.add(new Node(poped.x-1, poped.y, poped.distanceFromSource+1));
}
if((poped.x+1 > 0 && poped.x+1 < matrix.length) && matrix[poped.x+1][poped.y] != '0') {
list.add(new Node(poped.x+1, poped.y, poped.distanceFromSource+1));
}
if((poped.y-1 > 0 && poped.y-1 < matrix.length) && matrix[poped.x][poped.y-1] != '0') {
list.add(new Node(poped.x, poped.y-1, poped.distanceFromSource+1));
}
if((poped.y+1 > 0 && poped.y+1 < matrix.length) && matrix[poped.x][poped.y+1] != '0') {
list.add(new Node(poped.x, poped.y+1, poped.distanceFromSource+1));
}
return list;
}
}
class Node {
int x;
int y;
int distanceFromSource;
Node(int x, int y, int dis) {
this.x = x;
this.y = y;
this.distanceFromSource = dis;
}
}

You are essentially implementing BFS (Breadth first search) to detect the existence of a path from the source (S) to the destination (D). All you need to trace the path is maintain a parent Node in your Node definition.
Set the starting node's parent to null. Then, as your discover nodes in your BFS from the current node, set the parent of the discovered node to the current node.
Now, if your search is successful (i.e. you hit D in your search), just traverse the chain of parent nodes backwards from D until you hit S, throwing the visited parents into a stack.
Finally just keep popping the stack until it turns empty to get the nodes on the path from S to D.

you are getting only the distance because you are only returning the distance between source and Destination.
follow these to solve and print the route as well;
algo 1:-
just print the node value when you are updating the distance calculation.
algo 2:-
1. create a queue to store the nodes.
2. insert the node point of S to queue
3. keep adding to the node value to queue when you are adding the value to distance. unless reaching to 'D'
4. now just print the nodes from the queue which will print the path structure.

class Cello {
int row;
int col;
public Cello(int rowIndex, int colIndex) {
super();
this.row = rowIndex;
this.col = colIndex;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cello cell = (Cello) o;
return row == cell.row &&
col == cell.col;
}
#Override
public int hashCode() {
return Objects.hash(row, col);
}
}
public class ShortestPathWithParentChildMap {
public static void main(String[] args) {
char[][] grid2 = {{'S', '0', '1', '1'},
{'1', '1', '0', '1'},
{'0', '1', '1', '1'},
{'1', '0', 'D', '1'}};
List<int[]> path = shortestPath(grid2);
System.out.println("Path length:" + (path.size() - 1));
path.stream().forEach(i -> {
System.out.println("{" + i[0] + "," + i[1] + "}");
});
}
private static void bfs(char[][] grid, Cello start, List<int[]> path) {
int[] xDirs = new int[] {0,0,1, -1};
int[] yDirs = new int[] {1,-1, 0, 0};
Queue<Cello> bfsQueue = new LinkedList<>();
bfsQueue.add(start);
HashMap<Cello, Cello> parentMap = new HashMap<>();
boolean[][] visited = new boolean[grid.length][grid[0].length];
Cello endCell = null;
while(!bfsQueue.isEmpty()) {
boolean flag = false;
Cello from = bfsQueue.poll();
for (int k = 0; k < xDirs.length; ++k) {
int nextX = from.row + xDirs[k];
int nextY = from.col + yDirs[k];
if (nextX < 0 || nextX >= grid.length || nextY < 0
|| nextY >= grid[0].length || grid[nextX][nextY] == '0'
|| visited[nextX][nextY]) {
continue;
}
visited[nextX][nextY] = true;
Cello nextCell = new Cello(nextX, nextY);
bfsQueue.add(nextCell);
//we need a way to determine from where we have reached here
//storing the child to parent mapping, this will be used to retrieve the entire path
parentMap.put(nextCell, from);
//if (grid[nextX][nextY] == 'E')
if (grid[nextX][nextY] == 'D') {
endCell = new Cello(nextX, nextY);
flag = true;
break;
}
}
if (flag) {
break;
}
}
Stack<Cello> stack = new Stack<>();
stack.push(endCell);
//build the path from destination to source
while (true) {
Cello fromCell = parentMap.get(endCell);
stack.push(fromCell);
if (fromCell == start) break;
endCell = fromCell;
}
//reverse the above path and convert as List<int[]>
while (!stack.isEmpty()) {
Cello p = stack.pop();
path.add(new int[] {p.row, p.col});
}
}
private static List<int[]> shortestPath(char[][] grid) {
ArrayList<int[]> path = new ArrayList<>();
for (int i = 0; i < grid.length; ++i) {
for (int j = 0; j < grid[0].length; ++j) {
if (grid[i][j] == 'S') {
bfs(grid, new Cello(i, j), path);
}
}
}
return path;
}
}
Output is:
Path length:5
{0,0}
{1,0}
{1,1}
{2,1}
{2,2}
{3,2}

Related

How to find all the solutions for a NQueens problem given that one queen is fixed at some column?

This is all about the famous NQueens problem. My program works fine (backtrack approach). It finds all the solutions for a given board size.
Code is shown below.
I'm trying to modify the code so that I can find all the solutions for a given column of the first queen. I don't want to change the position of first queen. For an example it will provide me with the solution of
[2, 0, 3, 1, 4] and [2, 4, 1, 3, 0]
when I set the first queen at 2, board size 5 (third column, index starts from zero).
I tried this by setting different values for k (and board[k] as well) but doesn't quite reach the goal.
Any hints will be appreciated.
Here is my code. Removed details about place method since it shouldn't be changed to achieve my new goal.
public class NQueensAllSolutions
{
// Board size
static int size = 8;
// One dimensional array to store the column number for all queens.
static int[] board = new int[size];
// This method will check the validity for a new queen. works fine.
static boolean place(int k)
{
.
.
}
public static void main(String[] args)
{
int k;
long t=0; // for counting total found solutions
k = 0;
board[k] = -1;
while(k >= 0) {
board[k]++;
while(board[k] < size && !(place(k))) board[k]++;
if(board[k] < size) {
if(k == size-1) { // a solution is found.
t++;
//System.out.println("\n\nTotal: "+t+" --> "+Arrays.toString(board));
}
else {
k++; board[k] = -1;
}
}
else {
k--; // backtrack.
}
}
System.out.println("\n\nTotal: "+t);
}
}
Just keep k greater than 0 in the while loop:
import java.util.Arrays;
public class Queens
{
static int size = 5;
static int[] board = new int[size];
static boolean isValid(int k)
{
int c1 = board[k];
int c2 = board[k];
for(int r=k-1;r>=0;r--)
{
c1--;
c2++;
if(board[r] == board[k] || board[r] == c1 || board[r] == c2)
return false;
}
return true;
}
public static void main(String[] args)
{
int t = 0;
// Set the first queen position
board[0] = 2;
int k = 1;
board[k] = -1;
// k must stay greater than 0
while(k >= 1) {
board[k]++;
while(board[k] < size && !isValid(k))
board[k]++;
if(board[k] < size) {
if(k == size-1) {
t++;
System.out.println("Solution "+t+" --> "+Arrays.toString(board));
}
else {
k++;
board[k] = -1;
}
}
else {
k--;
}
}
}
}
Output:
Solution 1 --> [2, 0, 3, 1, 4]
Solution 2 --> [2, 4, 1, 3, 0]
UPDATE
Here is a generalized version that can force a queen at position (fixedRow, fixedCol).
The key change is the new getNextCol() method, which is used to get the next possible column for a queen. On row fixedRow, the only possible column is fixedCol. On the other rows, all columns are possible.
import java.util.Arrays;
public class Queens
{
static int size = 5;
static int fixedRow = 2;
static int fixedCol = 0;
static int[] board = new int[size];
static boolean isValid(int k)
{
int c1 = board[k];
int c2 = board[k];
for(int r=k-1;r>=0;r--)
{
c1--;
c2++;
if(board[r] == board[k] || board[r] == c1 || board[r] == c2)
return false;
}
return true;
}
static int getNextCol(int k, int col)
{
if(k == fixedRow) {
// Only one possible move on this row
return col == -1 ? fixedCol : size;
}
else {
// Try the next column
return col+1;
}
}
public static void main(String[] args)
{
int t = 0;
int k = 0;
board[k] = -1;
while(k >= 0) {
board[k] = getNextCol(k, board[k]);
while(board[k] < size && !isValid(k))
board[k] = getNextCol(k, board[k]);
if(board[k] < size) {
if(k == size-1) {
t++;
System.out.println("Solution "+t+" --> "+Arrays.toString(board));
}
else {
k++;
board[k] = -1;
}
}
else {
k--;
}
}
}
}
Output:
Solution 1 --> [1, 3, 0, 2, 4]
Solution 2 --> [4, 2, 0, 3, 1]

Why is the path reversed in this path-finding algorithm?

I'm creating a 2d top down game where the enemy AI is constantly following the player and avoiding obstacles. I did some research about path-finding algorithms and I decided to implement the breadth first search, but for some reason, the xy coordinates of the path are reversed, even though the grid is correct.
Code:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BFSTest {
// 1 = normal node
// 0 = obstacle
// S = start
// D = destination
private static char[][] nodes = {
{'S', '1', '1', '1'},
{'0', '0', '0', '1'},
{'0', '0', '0', '1'},
{'1', '1', '1', 'D'}
};
public static void main(String[] args) {
shortestPath();
}
public static List<Node> shortestPath() {
// key node, value parent
Map<Node, Node> parents = new HashMap<Node, Node>();
Node start = null;
Node end = null;
// find the start node
for (int row = 0; row < nodes.length; row++) {
for (int column = 0; column < nodes[row].length; column++) {
if (nodes[row][column] == 'S') {
start = new Node(row, column, nodes[row][column]);
break;
}
}
}
if (start == null) {
throw new RuntimeException("can't find start node");
}
// traverse every node using breadth first search until reaching the destination
List<Node> temp = new ArrayList<Node>();
temp.add(start);
parents.put(start, null);
boolean reachDestination = false;
while (temp.size() > 0 && !reachDestination) {
Node currentNode = temp.remove(0);
List<Node> children = getChildren(currentNode);
for (Node child : children) {
// Node can only be visited once
if (!parents.containsKey(child)) {
parents.put(child, currentNode);
char value = child.getValue();
if (value == '1') {
temp.add(child);
} else if (value == 'D') {
temp.add(child);
reachDestination = true;
end = child;
break;
}
}
}
}
if (end == null) {
throw new RuntimeException("can't find end node");
}
// get the shortest path
Node node = end;
List<Node> path = new ArrayList<Node>();
while (node != null) {
path.add(0, node);
node = parents.get(node);
}
printPath(path);
return path;
}
private static List<Node> getChildren(Node parent) {
List<Node> children = new ArrayList<Node>();
int x = parent.getX();
int y = parent.getY();
if (x - 1 >= 0) {
Node child = new Node(x - 1, y, nodes[x - 1][y]);
children.add(child);
}
if (y - 1 >= 0) {
Node child = new Node(x, y - 1, nodes[x][y - 1]);
children.add(child);
}
if (x + 1 < nodes.length) {
Node child = new Node(x + 1, y, nodes[x + 1][y]);
children.add(child);
}
if (y + 1 < nodes[0].length) {
Node child = new Node(x, y + 1, nodes[x][y + 1]);
children.add(child);
}
return children;
}
private static void printPath(List<Node> path) {
for (int row = 0; row < nodes.length; row++) {
for (int column = 0; column < nodes[row].length; column++) {
String value = nodes[row][column] + "";
// mark path with X
for (int i = 1; i < path.size() - 1; i++) {
Node node = path.get(i);
if (node.getX() == row && node.getY() == column) {
value = "X";
break;
}
}
if (column == nodes[row].length - 1) {
System.out.println(value);
} else {
System.out.print(value + " ");
}
}
}
System.out.println("Path: " + path);
}
}
class Node {
private int x;
private int y;
private char value;
public Node(int x, int y, char value) {
this.x = x;
this.y = y;
this.value = value;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public char getValue() {
return value;
}
#Override
public String toString() {
return "(x: " + x + " y: " + y + ")";
}
#Override
public int hashCode() {
return x * y;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (this.getClass() != o.getClass()) return false;
Node node = (Node) o;
return x == node.x && y == node.y;
}
/* Output:
* S X X X
* 0 0 0 X
* 0 0 0 X
* 1 1 1 D
* Path: [(x: 0 y: 0), (x: 0 y: 1), (x: 0 y: 2), (x: 0 y: 3), (x: 1 y: 3), (x: 2 y: 3), (x: 3 y: 3)]
*/
}
Thanks!
Think about this for a moment. Remember that your first array index is row, and the second is column. Now, in terms of conventional x and y:
the y index always refers to the row (because you count rows vertically);
the x index always refers to the column (because you count columns horizontally).
So, the correct way to index your grid is nodes[y][x]

Sliding window solution for min length substring with all distinct characters of the original string

Could anyone please tell where am I going wrong. Below is my code.
Here I am using sliding window solution with left and right as pointers to String.
"dist_count" contains the count for unique characters in string.
public class Solution {
public String findSubstring(String s) {
if (s == null || s.length() == 0)
return "";
int dist_count = 0;
int[] hash = new int[256]; // character hash
boolean[] visited = new boolean[256];
// record each character in s to hash
for (char c : s.toCharArray()) {
hash[c]++;
}
for (char c : s.toCharArray()) {
if (visited[c] == false) {
dist_count++;
visited[c] = true;
}
}
System.out.println(dist_count);
int left = 0, right = 0, count = 0;
int min_window = Integer.MAX_VALUE;
int startIndex = -1;
while (right < s.length()) {
if (hash[s.charAt(right)] >= 1) {
count++;
}
hash[s.charAt(right)]--;
right++;
if (count == dist_count) {
while (hash[s.charAt(left)] > 1) {
if (hash[s.charAt(left)] > 1)
hash[s.charAt(left)]--;
left++;
}
int window = right - left + 1;
if (min_window > window) {
min_window = window;
startIndex = left;
}
}
}
return s.substring(startIndex, startIndex + min_window);
}
public static void main(String args[]) {
Solution ob = new Solution();
String str = ob.findSubstring("abdacbfcabegbdfcadcefa");
System.out.println(str);
}
}
Your algorithm is right but there are some issues in implementation.
You should not use the hash leftover from original string. Look at the following code:
for(int i = 0; i < 256; ++i) hash[i] = 0; // Before entering while reset hash
// counters for sliding window
while (right < s.length()) {
if (hash[s.charAt(right)] == 0) { // Use hash here for counting frequency
count++;
}
hash[s.charAt(right)]--; // Consistently increment for right and decrement for left
Working code with above changes

Java Recursion return boolean

Given an array of integers f, I want to see if f[k]=k for some k in the array. I'm having some trouble because I would like to return on the left and right half of the array, but I am not sure how to go about doing that. This is what I have so far:
public class Find {
int a = 0;
public boolean find(int[] f) {
if(f.length < 1) {
return false;
}
System.out.println(f[0] + " " + a);
if(f.length == 1 && f[0] == a) {
return true;
}
if(f.length == 1 && f[0] != a) {
return false;
}
int[] L = Arrays.copyOfRange(f, 0, f.length / 2);
int[] R = Arrays.copyOfRange(f, f.length / 2, f.length);
find(L);
a++;
//find(R);
return find(R); //only finds in the right half...
}
public static void main(String[] args) {
Find F = new Find();
int[] test = {0, 13, 2, 3, 4};
System.out.println(F.find(test));
}
}
You could do the following, you currently search the left side but do not return the result:
return find(R) || find(L);

Java: search words in two dimensional array

I've been learning Java for about 4 months now, it's the first programming language I learn. For school we have to do a project, a console-based game. I chose for Boggle.
I have an ArrayList with dices, each one gets a random 'side up', and then the ArrayList gets shuffled and a two-dimensional array gets filled with the values of each side up. At this moment the Array is filled with Strings, chars may be a better option but something that's fairly easy to change.
The problem I'm facing is that I need to be able to find words in the array. Words in Boggle can go in any direction, each unique block can be used only once per word, but the path can cross, you can search diagonally too. I managed to find if the first letter is present in the array. If not than the search can be aborted, if it is present there needs to start a search that searches for the second character of the word, that has to be around the first character's block.
I did some math and found that it's always for example "i-1 and j-1" as the upper left corner of the surrounding blocks. I worked this out but can't seem to be able to find words... Also, if there are 2 "e"'s surrounding, I have no clue how to search for the word trying each "e". Here is my code so far:
This is my most important class at this moment, the class Gameboard
public class Gameboard {
private List<Dice> dices = new ArrayList<Dice>();
private final int boardSize;
private String[][] board;
private boolean [][] blocksAvailable;
private Random random = new Random();
public GameBoard() {
// Making the board with a given size (will be changeable later but is "4" for now)
boardSize = 4;
board = new String[boardSize][boardSize];
blocksAvailable = new boolean[boardSize][boardSize];
for(int i = 0; i < boardSize; i++) {
for(int j = 0; j < boardSize; j++) {
blocksAvailable[i][j] = true;
}
}
}
public String[][] getBoard() {
return board;
}
public int getFullSize() {
return boardSize*boardSize;
}
public void makeBoard() {
//random "side up" for each dice
for(int i = 0; i < dices.size(); i++) {
dices.get(i).setSideUp();
}
// Shuffle all dices
Collections.shuffle(dices);
// Fill the board with the values of the dices
int counter = 0;
for(int i = 0; i < boardSize; i++) {
for(int j = 0; j < boardSize; j++) {
board[i][j] = dices.get(counter++).getSideUp();
}
}
}
public String showBoard() {
//Show the board, each block divided by "|"
String str = "";
for(int i = 0; i < boardSize; i++) {
for(int j = 0; j < boardSize; j++) {
str += String.format("|%s|", board[i][j].toString());
if(j == 3) {
str += "\n";
}
}
}
return str;
}
public void addDices() {
dices.add(new dice("R", "I", "F", "O", "B", "X"));
dices.add(new dice("I", "F", "E", "H", "E", "Y"));
dices.add(new dice("D", "E", "N", "O", "W", "S"));
dices.add(new dice("U", "T", "O", "K", "N", "D"));
dices.add(new dice("H", "M", "S", "R", "A", "O"));
dices.add(new dice("L", "U", "P", "E", "T", "S"));
dices.add(new dice("A", "C", "I", "T", "O", "A"));
dices.add(new dice("Y", "L", "G", "K", "U", "E"));
dices.add(new dice("Q", "B", "M", "J", "O", "A"));
dices.add(new dice("E", "H", "I", "S", "P", "N"));
dices.add(new dice("V", "E", "T", "I", "G", "N"));
dices.add(new dice("B", "A", "L", "I", "Y", "T"));
dices.add(new dice("E", "Z", "A", "V", "N", "D"));
dices.add(new dice("R", "A", "L", "E", "S", "C"));
dices.add(new dice("U", "W", "I", "L", "R", "G"));
dices.add(new dice("P", "A", "C", "E", "M", "D"));
}
public boolean searchWord(String word) {
String wordUp = woord.toUpperCase();
String firstLetter = Character.toString(wordUp.charAt(0));
for(int i = 0; i < boardSize;i++) {
for(int j = 0; j < boardSize;j++) {
if(firstLetter.equals(board[i][j]) == true) {
int a = i;
int b = j;
String theLetter = "";
// First letter found, continue search
for(int h = 1; h < hetWoord.length(); h++) {
theLetter = Character.toString(wordUp.charAt(h));
int[] values = searchLetter(theLetter, a, b);
if(values[0] > -1) {
a = values[0];
b = values[1];
} else {
return false;
}
}
return true;
}
}
}
return false;
}
public int[] searchLetter(String letter, int i, int j) {
int[] values = new int[2];
try{if(board[i-1][j-1].equals(letter) && blocksAvailable[i-1][j-1] == true) {
values[0] = i-1;
values[1] = j-1;
blocksAvailable[i-1][j-1] = false;
} else if(board[i-1][j].equals(letter) && blocksAvailable[i-1][j] == true) {
values[0] = i-1;
values[1] = j;
blocksAvailable[i-1][j] = false;
} else if(board[i-1][j+1].equals(letter) && blocksAvailable[i-1][j+1] == true) {
values[0] = i-1;
values[1] = j+1;
blocksAvailable[i-1][j+1] = false;
} else if(board[i][j-1].equals(letter) && blocksAvailable[i][j-1] == true) {
values[0] = i;
values[1] = j-1;
blocksAvailable[i][j-1] = false;
} else if(board[i][j+1].equals(letter) && blocksAvailable[i][j+1] == true) {
values[0] = i;
values[1] = j+1;
blocksAvailable[i][j+1] = false;
} else if(board[i+1][j-1].equals(letter) && blocksAvailable[i+1][j-1] == true) {
values[0] = i+1;
values[1] = j-1;
blocksAvailable[i+1][j+1] = false;
} else if(board[i+1][j].equals(letter) && blocksAvailable[i+1][j] == true) {
values[0] = i+1;
values[1] = j;
blocksAvailable[i+1][j] = false;
} else if(board[i+1][j+1].equals(letter) && blocksAvailable[i+1][j+1] == true) {
values[0] = i+1;
values[1] = j+1;
blocksAvailable[i+1][j+1] = false;
} else {
values[0] = -1; // If not found, negative values, easy to check in searchWord if letter was found
values[1] = -1;
}}catch (ArrayIndexOutOfBoundsException e) {
}
return values;
}
}
This may get deleted, because I am not going to answer your question. But please, pretty please, use:
// instead of: board[i-1][j-1]
public String getBoardValue(int x, int y) {
if (x<0 || x>=boardSize) return "";
if (y<0 || y>=boardSize) return "";
return board[x][y];
}
Using such helper method you will
ensure no IndexArrayOutOfBoundsException is thrown
always have a non-null board value
Well, what you have asked is little complex.You have to look at all the possible directions for the word in your Array.I would suggest you should look at the mini projects and see their logic of evaluation.You can find many projects on google.Example http://1000projects.org/java-projects-gaming.html
I am not asking you to copy paste,but just to get an idea of doing things in a proper way.
Few years back i made an X-O game in c/c++, can give you the code(if you want) of checking the combinations of x's and o's(you can make out the code,the syntax in not much different).
Your method searchLetter can be rewritten to be more understandable.
public boolean isValid(int x, int y) {
return x>= 0 && x < boardSize && y >=0 && y < boardSize;
}
public int[] searchLetter(String letter, int i, int j) {
int[] values = new int[2];
//initialization as not found.
values[0] = -1;
values[1] = -1;
for(int ix = i-1; ix <= i+1 ; ix++){
for(int jx = j-1; jx <= j+1 ; jx++){
if(i == ix && j == jx)
//skip the cell from were the search is performed
continue;
if(isValid(ix,jx)
&& board[ix][jx].equals(letter)
&& blocksAvailable[ix][jx] == true) {
values[0] = ix;
values[1] = jx;
blocksAvailable[ix][jx] = false;
//early return
return values;
}
}
return values;
}
Even without comments, a reader has the hint that this is some kind of neighboring search.
The fact is your algorithm should return a list of possible next positions.The code above return the first occurrence. If you put each pair of indices in a list rather than returning the first one, you will see all the candidate letters for your word. And while doing so, you will be implementing the getNeighbour or adjancentVertexes of a Breath first search.
One tip: Once you find the initial letter, instead of looking for the next letters one by one, just compute the 8 possible words of same length you can get from that point. You might find less than 8 depending on the position of that letter on the board.
Once you are able to figure this out for one direction, it is going to be easier to translate that logic to the rest of possible directions.
Then you just need to compare those words and if none of then match, find the initial letter and repeat the same process.
I got asked this on an interview, I didn't have much time left so I explained my interviewer how I would approach this problem. He seemed ok with my answer and didn't even ask me to try to code it, probably because he knew this problem is not as trivial as it might look and we didn't have enough time. I gave it a try at home after my interview finished but my solution had some bugs. Kept thinking about it and came up with the ideas I posted previously, then noticed this post is 3 years old so I decided to code it. This solution works but has room for improvements:
import java.util.LinkedHashSet;
import java.util.Set;
public class StringsArrays {
public static void main(String[] args) {
final char[][] matrix = new char[4][4];
matrix[0] = new char[] { 'G', 'A', 'B', 'C' };
matrix[1] = new char[] { 'O', 'O', 'E', 'F' };
matrix[2] = new char[] { 'O', 'H', 'O', 'I' };
matrix[3] = new char[] { 'J', 'K', 'L', 'D' };
System.out.println(search("JOOG", matrix)); //N
System.out.println(search("BEOL", matrix)); //S
System.out.println(search("AB", matrix)); //E
System.out.println(search("FE", matrix)); //W
System.out.println(search("HEC", matrix)); //NE
System.out.println(search("DOOG", matrix)); //NW
System.out.println(search("GOOD", matrix)); //SE
System.out.println(search("FOK", matrix)); //SW
System.out.println(search("HO", matrix));
}
public static boolean search(final String word, char[][] matrix) {
final char firstLetter = word.charAt(0);
for (int y = 0; y < matrix.length; y++) {
for (int x = 0; x < matrix[y].length; x++) {
if (matrix[y][x] == firstLetter) {
final Set<String> words = readInAllDirections(word.length(), x, y, matrix);
if (words.contains(word)) {
return true;
}
}
}
}
return false;
}
enum Direction {
NORTH, SOUTH,
EAST, WEST,
NORTH_EAST, NORTH_WEST,
SOUTH_EAST, SOUTH_WEST
}
private static Set<String> readInAllDirections(final int length, final int x, final int y, final char[][] matrix) {
final Set<String> words = new LinkedHashSet<>();
for (final Direction direction : Direction.values()) {
words.add(readWord(length, x, y, matrix, direction));
}
return words;
}
private static String readWord(final int length, final int xBegin, final int yBegin, final char[][] matrix, final Direction direction) {
final int xEnd = getXEnd(xBegin, length, direction);
final int yEnd = getYEnd(yBegin, length, direction);
int x;
int y;
final StringBuilder matrixWord = new StringBuilder();
if (direction == Direction.SOUTH) {
if (yEnd > matrix.length-1) {
return null;
}
for (y = yBegin; y <= yEnd; y++) {
matrixWord.append(matrix[y][xBegin]);
}
}
if (direction == Direction.NORTH) {
if (yEnd < 0) {
return null;
}
for (y = yBegin; y >= yEnd; y--) {
matrixWord.append(matrix[y][xBegin]);
}
}
if (direction == Direction.EAST) {
if (xEnd > matrix[yBegin].length-1) {
return null;
}
for (x = xBegin; x <= xEnd; x++) {
matrixWord.append(matrix[yBegin][x]);
}
}
if (direction == Direction.WEST) {
if (xEnd < 0) {
return null;
}
for (x = xBegin; x >= xEnd; x--) {
matrixWord.append(matrix[yBegin][x]);
}
}
if (direction == Direction.SOUTH_EAST) {
if (yEnd > matrix.length-1 || xEnd > matrix[yBegin].length-1) {
return null;
}
x = xBegin;
y = yBegin;
while (y <= yEnd && x <= xEnd) {
matrixWord.append(matrix[y][x]);
y++;
x++;
}
}
if (direction == Direction.SOUTH_WEST) {
if (yEnd > matrix.length-1 || xEnd < 0) {
return null;
}
x = xBegin;
y = yBegin;
while (y <= yEnd && x >= xEnd) {
matrixWord.append(matrix[y][x]);
y++;
x--;
}
}
if (direction == Direction.NORTH_EAST) {
if (yEnd < 0 || xEnd > matrix[yBegin].length-1) {
return null;
}
x = xBegin;
y = yBegin;
while (y >= yEnd && x <= xEnd) {
matrixWord.append(matrix[y][x]);
y--;
x++;
}
}
if (direction == Direction.NORTH_WEST) {
if (yEnd < 0 || xEnd < 0) {
return null;
}
x = xBegin;
y = yBegin;
while (y >= yEnd && x >= xEnd) {
matrixWord.append(matrix[y][x]);
y--;
x--;
}
}
return matrixWord.toString();
}
private static int getYEnd(final int y, final int length, final Direction direction) {
if (direction == Direction.SOUTH || direction == Direction.SOUTH_EAST || direction == Direction.SOUTH_WEST) {
// y0 + length + ? = y1
return y + length - 1;
}
if (direction == Direction.NORTH || direction == Direction.NORTH_EAST || direction == Direction.NORTH_WEST) {
// y0 - length + ? = y1
return y - length + 1;
}
// direction == Direction.EAST || direction == Direction.WEST)
return y;
}
private static int getXEnd(final int x, final int length, final Direction direction) {
if (direction == Direction.EAST || direction == Direction.NORTH_EAST || direction == Direction.SOUTH_EAST) {
// x0 + length + ? = x1
return x + length - 1;
}
if (direction == Direction.WEST || direction == Direction.NORTH_WEST || direction == Direction.SOUTH_WEST) {
// x0 - length + ? = x1
return x - length + 1;
}
// direction == Direction.NORTH || direction == Direction.SOUTH)
return x;
}
}

Categories