In a number maze, a player always starts from the square at the upper left and makes a certain number of moves which will take him/her to the square marked Goal if a solution exist. The value in each cell in the maze indicates how far a player must move horizontally or vertically from its current position.
My task is to find out if the shortest path to the cell labeled “Goal” and print it.
Input
the maze is in the form of square 2D array. The goal square is indicated by the number -1 in the maze description.
Output
For the maze, output the solution of the maze or the phrase “No Solution Possible.” Solutions should be output as a list of square coordinates in the format “(Row, Column)”, in the order in which they are visited from the start to the goal, including the starting cell. You will need to report the shortest solution from start to the goal. The shortest solution will be unique.
I have tried some solution, but I think there is problem that is the solution is always the first path I found not the shortest..
public class findShoretstPath
{
private static Stack<Node> stack = new Stack<>();
private static class Node
{
private int[] coordinate = new int[2];
private int data;
private Node Right, Left, Top, Bottom;
public Node(){}
}
public static boolean isValid(int[][] a, int x, int y)
{
if(x >= 0 && x < a.length && y >= 0 && y < a.length)
return true;
return false;
}
public static Node[][] nodeArray(int[][] a)
{
Node[][] nodeA = new Node[a.length][a.length];
for(int i = 0; i<nodeA.length; i++)
for(int j = 0; j<nodeA[i].length; j++)
{
nodeA[i][j] = new Node();
nodeA[i][j].coordinate[0] = i;
nodeA[i][j].coordinate[1] = j;
nodeA[i][j].data = a[i][j];
}
for(int i = 0; i<nodeA.length; i++)
for(int j = 0; j<nodeA[i].length; j++)
{
if(isValid(a, i, j+nodeA[i][j].data))
nodeA[i][j].Right = nodeA[i][j+nodeA[i][j].data];
if(isValid(a, i, j-nodeA[i][j].data))
nodeA[i][j].Left = nodeA[i][j-nodeA[i][j].data];
if(isValid(a, i+nodeA[i][j].data, j))
nodeA[i][j].Bottom = nodeA[i+nodeA[i][j].data][j];
if(isValid(a, i-nodeA[i][j].data, j))
nodeA[i][j].Top = nodeA[i-nodeA[i][j].data][j];
}
return nodeA;
}
public static boolean findPath(Node[][] s, int[][] t, int x, int y)
{
boolean b = false;
if(t[x][y] == 0)
{
t[x][y] = 1;
if(s[x][y].data == -1) b = true;
else
{
if(s[x][y].Right != null) b = findPath(s, t, x, y+s[x][y].data);
if(!b && s[x][y].Bottom != null) b = findPath(s, t, x+s[x][y].data, y);
if(!b && s[x][y].Left != null) b = findPath(s, t, x, y-s[x][y].data);
if(!b && s[x][y].Top != null) b = findPath(s, t, x-s[x][y].data, y);
}
if(b) stack.add(s[x][y]);
}
return b;
}
public static void main(String[] args)
{
int[][] maze = {{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,3},
{4,1,1,3,-1}};
Node[][] net = nodeArray(maze);
int[][] path = new int[maze.length][maze[0].lenght];
if(findPath(net, path, 0, 0))
{
Node temp;
while(!stack.isEmpty())
{
temp = stack.pop();
System.out.print("("+temp.coordinate[0]+" "+temp.coordinate[1]+") ");
}
}
else System.out.println("No Solution Possible.");
}
}
for this example the output should be:
(0 0) (1 0) (2 0) (3 0) (4 0) (4 4)
but I have this output:
(0 0) (0 1) (0 2) (0 3) (0 4) (1 4) (2 4) (3 4) (3 1) (3 2) (3 3) (4 3) (4 0) (4 4)
Please, any help how to fix the code so the solution will be the shortest path?!
After searching about BFS, now I know the difference between DFS and BFS.
DFS algorithm travels a path from the source to the last node, if the goal is found stop, else try another path again from the source to the last node, and so until the goal is reached. While BFS algorithm travels from the source to the level below, if the goal is found stop, else go to the next level and so on..
For my problem, BFS is a suitable algorithm to find the shortest path.
The code after some modifications:
public class findShoretstPath
{
private static class Node
{
private int[] coordinate = new int[2];
private int data;
private Node Right, Left, Top, Bottom;
public Node(){}
}
public static boolean isLinked(Node s, Node d) //method to determine if the node d is linked to the node s
{
if(d.Right == s) return true;
if(d.Bottom == s) return true;
if(d.Left == s) return true;
if(d.Top == s) return true;
return false;
}
public static boolean isValid(int[][] a, int x, int y)
{
if(x >= 0 && x < a.length && y >= 0 && y < a.length)
return true;
return false;
}
public static Node[][] nodeArray(int[][] a)
{
Node[][] nodeA = new Node[a.length][a.length];
for(int i = 0; i<nodeA.length; i++)
for(int j = 0; j<nodeA[i].length; j++)
{
nodeA[i][j] = new Node();
nodeA[i][j].coordinate[0] = i;
nodeA[i][j].coordinate[1] = j;
nodeA[i][j].data = a[i][j];
}
for(int i = 0; i<nodeA.length; i++)
for(int j = 0; j<nodeA[i].length; j++)
{
if(isValid(a, i, j+nodeA[i][j].data))
nodeA[i][j].Right = nodeA[i][j+nodeA[i][j].data];
if(isValid(a, i, j-nodeA[i][j].data))
nodeA[i][j].Left = nodeA[i][j-nodeA[i][j].data];
if(isValid(a, i+nodeA[i][j].data, j))
nodeA[i][j].Bottom = nodeA[i+nodeA[i][j].data][j];
if(isValid(a, i-nodeA[i][j].data, j))
nodeA[i][j].Top = nodeA[i-nodeA[i][j].data][j];
}
return nodeA;
}
public static void shortestPath(Node[][] nodes, int x, int y)
{
Stack<Node> stack = new Stack<>();
Queue<Node> queue = new LinkedList<>();
int[][] path = new int[nodes.length][nodes[0].length];
boolean b = false;
int level = 1;//to keep tracking each level viseted
queue.add(nodes[x][y]);
path[x][y] = level;
while(!queue.isEmpty())
{
Node temp;
level++;
int size = queue.size();
for(int i = 0; i<size; i++)
{
temp = queue.remove();
if(temp.data == -1) {b = true; break;}
if(temp.Right != null && path[temp.Right.coordinate[0]][temp.Right.coordinate[1]] == 0)
{
queue.add(temp.Right);
path[temp.Right.coordinate[0]][temp.Right.coordinate[1]] = level;
}
if(temp.Bottom != null && path[temp.Bottom.coordinate[0]][temp.Bottom.coordinate[1]] == 0)
{
queue.add(temp.Bottom);
path[temp.Bottom.coordinate[0]][temp.Bottom.coordinate[1]] = level;
}
if(temp.Left != null && path[temp.Left.coordinate[0]][temp.Left.coordinate[1]] == 0)
{
queue.add(temp.Left);
path[temp.Left.coordinate[0]][temp.Left.coordinate[1]] = level;
}
if(temp.Top != null && path[temp.Top.coordinate[0]][temp.Top.coordinate[1]] == 0)
{
queue.add(temp.Top);
path[temp.Top.coordinate[0]][temp.Top.coordinate[1]] = level;
}
}
if(b) break;
}
if(b)
{
int x1 = 0, y1 = 0;
for(int i = 0; i<nodes.length; i++)// to locate the position of the goal
for(int j = 0; j<nodes.length; j++)
if(nodes[i][j].data == -1)
{
x1 = i; y1 = j;
}
stack.add(nodes[x1][y1]);
int d = path[x1][y1];
while(d > 0)//go back from the goal to the source
{
for(int i = 0; i<path.length; i++)
{
if(path[x1][i] == d-1 && isLinked(nodes[x1][y1], nodes[x1][i]))
{
stack.add(nodes[x1][i]);
y1 = i;
break;
}
else if(path[i][y1] == d-1 && isLinked(nodes[x1][y1], nodes[i][y1]))
{
stack.add(nodes[i][y1]);
x1 = i;
break;
}
}
d--;
}
Node temp;
int stackSize = stack.size();
for(int i = 0; i<stackSize; i++)// print the final result
{
temp = stack.pop();
System.out.print("("+temp.coordinate[0]+" "+temp.coordinate[1]+") ");
}
}
else System.out.print("No Solution Possible.");
}
public static void main(String[] args)
{
int[][] maze = {{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,1},
{1,1,1,1,3},
{4,1,1,3,-1}};
Node[][] net = nodeArray(maze);
shortestPath(net, 0, 0));
System.out.println("");
}
}
and the output now is:
(0 0) (1 0) (2 0) (3 0) (4 0) (4 4)
I have looked at this question here I tried most of the code samples from there but when i use it in my code it just skips the algorithm.
I have this DFS algorithm that uses stack, I am getting a EmptyStackException, I have debugged the algorithm and after first recursive search the stack is empty, the first search works but then the size of stack is set to 0, What am I missing here? github
How can I make sure that the stack is not empty after the first search?
The line that I get the exception on is while(true){AddBridges state = gameTree.peek(); ...
I am using a 2d Array to generate the nodes at random from 0 to 4 0 = null 1-4 = island The array generates Random Integers every time the user starts the game, could that cause the Algorithm to brake,
After a weekend of debugging I found that the algorithm sometimes brakes after 4-6 searches, and sometimes breaks after the first search.
public int[][] debug_board_state_easy = new int[4][4];
//This Generates random 2d array
private void InitializeEasy() {
Random rand = new Random();
setCurrentState(new State(WIDTH_EASY));
for (int row = 0; row < debug_board_state_easy.length; row++) {
for (int column = 0; column < debug_board_state_easy[row].length; column++) {
debug_board_state_easy[row][column] = Integer.valueOf(rand.nextInt(5));
}
}
for (int row = 0; row < debug_board_state_easy.length; row++) {
for (int column = 0; column < debug_board_state_easy[row].length; column++) {
System.out.print(debug_board_state_easy[row][column] + " ");
}
System.out.println(debug_board_state_easy);
}
//I am applying the search algorithm here...
this.search();
for (int row = 0; row < WIDTH_EASY; ++row) {
for (int column = 0; column < WIDTH_EASY; ++column) {
getCurrentState().board_elements[row][column] = new BoardElement();
getCurrentState().board_elements[row][column].max_connecting_bridges = Integer.valueOf(debug_board_state_easy[row][column]);
getCurrentState().board_elements[row][column].row = row;
getCurrentState().board_elements[row][column].col = column;
if (getCurrentState().board_elements[row][column].max_connecting_bridges > 0) {
getCurrentState().board_elements[row][column].is_island = true;
}
}
}
}
void search() {
Map<Point, List<Direction>> remainingOptions = new HashMap<>();
Stack<Land> gameTree = new Stack<>();
gameTree.push(new AddBridges(debug_board_state_easy));
while(true) {
AddBridges state = gameTree.peek();
int[] p = state.lowestTodo();
if (p == null)
System.out.println("solution found");
// move to next game state
int row = p[0];
int column = p[1];
System.out.println("expanding game state for node at (" + row + ", " + column + ")");
List<Direction> ds = null;
if(remainingOptions.containsKey(new Point(row,column)))
ds = remainingOptions.get(new Point(row,column));
else{
ds = new ArrayList<>();
for(Direction dir : Direction.values()) {
int[] tmp = state.nextIsland(row, column, dir);
if(tmp == null)
continue;
if(state.canBuildBridge(row,column,tmp[0], tmp[1]))
ds.add(dir);
}
remainingOptions.put(new Point(row,column), ds);
}
// if the node can no longer be expanded, and backtracking is not possible we quit
if(ds.isEmpty() && gameTree.isEmpty()){
System.out.println("no valid configuration found");
return;
}
// if the node can no longer be expanded, we need to backtrack
if(ds.isEmpty()){
gameTree.pop();
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue;
}
Direction dir = ds.remove(0);
System.out.println("connecting " + dir.name());
remainingOptions.put(new Point(row,column), ds);
AddBridgesnextState = new AddBridges(state);
int[] tmp = state.nextIsland(row,column,dir);
nextState.connect(row,column, tmp[0], tmp[1]);
gameTree.push(nextState);
}
}
}
Add bridges class
public class AddBridges {
private int[][] BRIDGES_TO_BUILD;
private boolean[][] IS_ISLAND;
private Direction[][] BRIDGES_ALREADY_BUILT;
public Land(int[][] bridgesToDo){
BRIDGES_TO_BUILD = copy(bridgesToDo);
int numberRows = bridgesToDo.length;
int numberColumns = bridgesToDo[0].length;
BRIDGES_ALREADY_BUILT = new Direction[numberRows][numberColumns];
IS_ISLAND = new boolean[numberRows][numberColumns];
for(int i=0;i<numberRows;i++) {
for (int j = 0; j < numberColumns; j++) {
BRIDGES_ALREADY_BUILT[i][j] = null;
IS_ISLAND[i][j] = bridgesToDo[i][j] > 0;
}
}
}
public AddBridges (AddBridges other){
BRIDGES_TO_BUILD = copy(other.BRIDGES_TO_BUILD);
int numberRows = BRIDGES_TO_BUILD.length;
int numberColumns = BRIDGES_TO_BUILD[0].length;
BRIDGES_ALREADY_BUILT = new Direction[numberRows][numberColumns];
IS_ISLAND = new boolean[numberRows][numberColumns];
for(int i=0;i<numberRows;i++) {
for (int j = 0; j < numberColumns; j++) {
BRIDGES_ALREADY_BUILT[i][j] = other.BRIDGES_ALREADY_BUILT[i][j];
IS_ISLAND[i][j] = other.IS_ISLAND[i][j];
}
}
}
public int[] next(int r, int c, Direction dir){
int numberRows = BRIDGES_TO_BUILD.length;
int numberColumns = BRIDGES_TO_BUILD[0].length;
// out of bounds
if(r < 0 || r >=numberRows || c < 0 || c >= numberColumns)
return null;
// motion vectors
int[][] motionVector = {{-1, 0},{0,1},{1,0},{0,-1}};
int i = Arrays.asList(Direction.values()).indexOf(dir);
// calculate next
int[] out = new int[]{r + motionVector[i][0], c + motionVector[i][1]};
r = out[0];
c = out[1];
// out of bounds
if(r < 0 || r >=numberRows || c < 0 || c >= numberColumns)
return null;
// return
return out;
}
public int[] nextIsland(int row, int column, Direction dir){
int[] tmp = next(row,column,dir);
if(tmp == null)
return null;
while(!IS_ISLAND[tmp[0]][tmp[1]]){
tmp = next(tmp[0], tmp[1], dir);
if(tmp == null)
return null;
}
return tmp;
}
public boolean canBuildBridge(int row0, int column0, int row1, int column1){
if(row0 == row1 && column0 > column1){
return canBuildBridge(row0, column1, row1, column0);
}
if(column0 == column1 && row0 > row1){
return canBuildBridge(row1, column0, row0, column1);
}
if(row0 == row1){
int[] tmp = nextIsland(row0, column0, Direction.EAST);
if(tmp == null)
return false;
if(tmp[0] != row1 || tmp[1] != column1)
return false;
if(BRIDGES_TO_BUILD[row0][column0] == 0)
return false;
if(BRIDGES_TO_BUILD[row1][column1] == 0)
return false;
for (int i = column0; i <= column1 ; i++) {
if(IS_ISLAND[row0][i])
continue;
if(BRIDGES_ALREADY_BUILT[row0][i] == Direction.NORTH)
return false;
}
}
if(column0 == column1){
int[] tmp = nextIsland(row0, column0, Direction.SOUTH);
if(tmp == null)
return false;
if(tmp[0] != row1 || tmp[1] != column1)
return false;
if(BRIDGES_TO_BUILD[row0][column0] == 0 || BRIDGES_TO_BUILD[row1][column1] == 0)
return false;
for (int i = row0; i <= row1 ; i++) {
if(IS_ISLAND[i][column0])
continue;
if(BRIDGES_ALREADY_BUILT[i][column0] == Direction.EAST)
return false;
}
}
// default
return true;
}
public int[] lowestTodo(){
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
int[] out = {0, 0};
for (int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
if(BRIDGES_TO_BUILD[i][j] == 0)
continue;
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0)
out = new int[]{i, j};
if (BRIDGES_TO_BUILD[i][j] < BRIDGES_TO_BUILD[out[0]][out[1]])
out = new int[]{i, j};
}
}
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0) {
return null;
}
return out;
}
#TargetApi(Build.VERSION_CODES.GINGERBREAD)
private int[][] copy(int[][] other){
int[][] out = new int[other.length][other.length == 0 ? 0 : other[0].length];
for(int r=0;r<other.length;r++)
out[r] = Arrays.copyOf(other[r], other[r].length);
return out;
}
public void connect(int r0, int c0, int r1, int c1){
if(r0 == r1 && c0 > c1){
connect(r0, c1, r1, c0);
return;
}
if(c0 == c1 && r0 > r1){
connect(r1, c0, r0, c1);
return;
}
if(!canBuildBridge(r0, c0, r1, c1))
return;
BRIDGES_TO_BUILD[r0][c0]--;
BRIDGES_TO_BUILD[r1][c1]--;
if(r0 == r1){
for (int i = c0; i <= c1 ; i++) {
if(IS_ISLAND[r0][i])
continue;
BRIDGES_ALREADY_BUILT[r0][i] = Direction.EAST;
}
}
if(c0 == c1){
for (int i = r0; i <= r1 ; i++) {
if(IS_ISLAND[i][c0])
continue;
BRIDGES_ALREADY_BUILT[i][c0] = Direction.NORTH;
}
}
}
}
One part of your question stood out to me as the root of the problem: "What am I missing here"? Unit tests, unless I just didn't see them in your project.
Questions like "the array generates Random Integers every time the user starts the game, could that cause the Algorithm to brake?", are the reason unit tests exist, along with the following:
In the course of writing tests on sections of code that don't end up
being the problem, you'll prove definitively that they aren't the
problem.
If the code you're working with can't be tested as-is, or
is "too complex" to test, re-writing it will make you a better
designer and will often lead to "I can't believe I didn't see that"
moments.
When you refactor this program (reduce complexity, rename variables to make them easier to understand, etc), you'll be notified immediately if something breaks
and you won't have to spend another weekend trying to figure it out.
As an example, instead of randomizing the board within the method that searches it, randomize it elsewhere and then drop it into that method as an argument (or into the class' constructor). That way, you can initialize your own test board(s) with your own supplied values and see if some boards work better than others and why. Split up larger methods into smaller methods, each with their own parameters and tests. Aim to make a program out of smaller confirmed-working pieces, instead of making a huge thing and then wondering if the problem is the small part you think it is or something else.
You'll save so much time and frustration in the long run, and you'll end up leagues ahead of those who code for hours and then debug for hours.
There's a lot going on in the code, but the first thing I notice might help:
// if the node can no longer be expanded, we need to backtrack
if(ds.isEmpty()){
gameTree.pop();
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue;
}
you pop from the stack, and continue onto the next while(true) iteration, at that point, there may be nothing on the stack since you have not added anything else.
I agree with #Rosa -
The EmptyStackException should occur when removing or looking up empty Stack-
======Iteration/State in code ======
**if(ds.isEmpty()){** //HashMap isEmpty = true and gameTree.size() = 1
gameTree.pop(); // After removing element gameTree.size() = 0 (no elements in stack to peek or pop)
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue; //Skip all the instruction below this, continue to next iteration
}
========Next iteration========
while(true){
AddBridges state = gameTree.peek(); // gameTree.size() = 0 and a peek
operation shall fail and program will return EmptyStackException.
Required isEmpty check -
if(gameTree.isEmpty()){
System.out.println("no valid configuration found");
return;
}
AddBridges state = gameTree.peek();
As, no actions have been performed by function but while loop. In case other instcutions to be processed , a "break" is required.
The problem I'm trying to solve is a standard interview question. Given a boolean matrix find the path from the starting point to the finishing point.
The start point is assumed the left top corner
The finishing point the right bottom corner.
Only grids with 0 can be moved into.
No diagonal moves are allowed.
Here's my code.
public class PathFinder {
public static ArrayList<Pair> dfs(int[][] arr, int row, int col, Pair sp, Pair fp){
int[][] check = new int[row][col];
ArrayList<Pair> path = new ArrayList<>();
dfs(arr, row, col, path, check, sp, fp);
return path;
}
private static void dfs(int[][] arr, int row, int col, ArrayList<Pair> path, int[][] check, Pair sp, Pair fp){
if(sp.getRow() == fp.getRow() && sp.getCol() == fp.getCol()) return;
if((sp.getRow() +1 < row) &&(arr[sp.getRow() +1][sp.getCol()] == 0) && (check[sp.getRow()+1][sp.getCol()] == 0)){
check[sp.getRow()+1][sp.getCol()] = 1;
path.add(new Pair(sp.getRow()+1, sp.getCol()));
dfs(arr, row, col, path, check, new Pair(sp.getRow()+1, sp.getCol()), fp);
}else if((sp.getRow() -1 >= 0) &&(arr[sp.getRow() -1][sp.getCol()] == 0) && (check[sp.getRow()-1][sp.getCol()] == 0)){
check[sp.getRow()-1][sp.getCol()] = 1;
path.add(new Pair(sp.getRow()-1, sp.getCol()));
dfs(arr, row, col, path, check, new Pair(sp.getRow()-1, sp.getCol()), fp);
}else if((sp.getCol() +1 < col) &&(arr[sp.getRow()][sp.getCol() +1] == 0) && (check[sp.getRow()][sp.getCol()+1] == 0)){
check[sp.getRow()][sp.getCol()+1] = 1;
path.add(new Pair(sp.getRow(), sp.getCol()+1));
dfs(arr, row, col, path, check, new Pair(sp.getRow(), sp.getCol()+1), fp);
}else if((sp.getCol() -1 >= 0) &&(arr[sp.getRow()][sp.getCol() -1] == 0) && (check[sp.getRow()][sp.getCol()-1] == 0)) {
check[sp.getRow()][sp.getCol() - 1] = 1;
path.add(new Pair(sp.getRow(), sp.getCol() - 1));
dfs(arr, row, col, path, check, new Pair(sp.getRow(), sp.getCol() - 1), fp);
}
}
public static void printPath(ArrayList<Pair> list){
for(Iterator itr = list.iterator(); itr.hasNext();){
Pair p = (Pair) itr.next();
System.out.println(p.getRow()+","+p.getCol());
}
}
}
Here's my Pair
public class Pair {
private int row;
private int col;
public Pair(int row, int col){
this.row = row;
this.col = col;
}
public int getRow(){
return row;
}
public int getCol(){
return col;
}
}
And here's my calling code.
public class Main {
public static void printArray(int[][] arr, int row, int col){
for (int i = 0; i < row; i++) {
for (int j = 0; j <col ; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
// write your code here
int row = 5;
int col = 7;
int[][] matrix = new int[row][col];
matrix[0][1] = 1;
matrix[0][3] = 1;
matrix[0][5] = 1;
matrix[1][1] = 1;
matrix[1][3] = 1;
matrix[1][6] = 1;
matrix[2][1] = 1;
matrix[2][2] = 1;
matrix[2][6] = 1;
matrix[3][3] = 1;
matrix[3][5] = 1;
matrix[3][6] = 1;
matrix[4][0] = 1;
printArray(matrix, row, col);
ArrayList<Pair> list = PathFinder.dfs(matrix, row, col, new Pair(0,0), new Pair(row-1, col-1));
PathFinder.printPath(list);
}
}
The issue is that this depth-first-search only works for specific cases. Can someone help me modify the code so that it works for all cases. Please bear in mind I don't want a breath-first search.
Here is a solution with the use of a Stack containing subpaths between junctions and a self implemented linked list of Pairs. The already visited fields are saved in the matrix. At the end the matrix is printed again, where the result-fields (found path) have the value 3 and the other visited fields have the value 2.
public class Pair {
private int row;
private int col;
private Pair next;
public Pair(int row, int col){
this.row = row;
this.col = col;
}
public int getRow(){
return row;
}
public int getCol(){
return col;
}
public Pair getNext() {
return next;
}
public void setNext(Pair next) {
this.next = next;
}
}
///////////////////////
import java.util.*;
public class PathFinder {
private int[][] arr;
private int rowCount;
private int colCount;
private Stack<Pair> junctions = new Stack<>();
public PathFinder(int[][] arr){
this.arr = arr;
this.rowCount = arr.length;
if(rowCount > 0) {
this.colCount = arr[0].length;
}
}
public Pair dfs(Pair sp){
int actualRow = sp.getRow();
int actualCol = sp.getCol();
//we where already here
arr[actualRow][actualCol] = 2;
if(actualRow >= rowCount - 1 && actualCol >= colCount - 1) {
//ready
return sp;
}
boolean deeper = actualRow +1 < rowCount && arr[actualRow +1][actualCol] == 0;
boolean left = actualCol -1 >= 0 && arr[actualRow][actualCol -1] == 0;
boolean right = actualCol +1 < colCount && arr[actualRow][actualCol +1] == 0;
boolean up = actualRow -1 >= 0 && arr[actualRow-1][actualCol] == 0;
//test for junctions
int possibilities = 0;
if(left){
possibilities++;
}
if(right) {
possibilities++;
}
if(deeper){
possibilities++;
}
if(up){
possibilities++;
}
if(possibilities > 1) {
this.junctions.push(sp);
}
Pair nextPair;
if(deeper){
nextPair = new Pair(actualRow + 1, actualCol);
} else if(left) {
nextPair = new Pair(actualRow, actualCol-1);
} else if(right) {
nextPair = new Pair(actualRow, actualCol+1);
} else if(up) {
nextPair = new Pair(actualRow-1, actualCol);
} else {
if(!this.junctions.empty()) {
Pair lastJunction = this.junctions.pop();
lastJunction.setNext(null);
return dfs(lastJunction);
}
return sp;
}
sp.setNext(nextPair);
return dfs(nextPair);
}
}
/////////////////////
public class Main {
public static void printArray(int[][] arr, int row, int col){
for (int i = 0; i < row; i++) {
for (int j = 0; j <col ; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
int rowCount = 6;
int colCount = 8;
int[][] matrix = new int[rowCount][colCount];
matrix[0] = new int[]{0, 1, 0, 1, 0, 0, 0, 1};
matrix[1] = new int[]{0, 1, 0, 0, 0, 1, 0, 0};
matrix[2] = new int[]{0, 0, 0, 1, 0, 1, 0, 0};
matrix[3] = new int[]{0, 1, 1, 1, 1, 0, 0, 1};
matrix[4] = new int[]{0, 0, 0, 0, 0, 1, 0, 0};
matrix[5] = new int[]{0, 1, 0, 1, 0, 0, 1, 0};
printArray(matrix, rowCount, colCount);
Pair pair = new Pair(0,0);
PathFinder finder = new PathFinder(matrix);
Pair finish = finder.dfs(pair);
if(finish.getRow() == rowCount-1 && finish.getCol() == colCount -1) {
while( pair != null){
System.out.println(pair.getRow()+","+pair.getCol());
matrix[pair.getRow()][pair.getCol()] = 3;
pair = pair.getNext();
}
} else {
System.out.println("no path found");
}
printArray(matrix, rowCount, colCount);
}
}
The task is:
A non-empty zero-indexed string S is given. String S consists of N characters from the set of upper-case English letters A, C, G, T.
This string actually represents a DNA sequence, and the upper-case letters represent single nucleotides.
You are also given non-empty zero-indexed arrays P and Q consisting of M integers. These arrays represent queries about minimal nucleotides. We represent the letters of string S as integers 1, 2, 3, 4 in arrays P and Q, where A = 1, C = 2, G = 3, T = 4, and we assume that A < C < G < T.
Query K requires you to find the minimal nucleotide from the range (P[K], Q[K]), 0 ≤ P[i] ≤ Q[i] < N.
For example, consider string S = GACACCATA and arrays P, Q such that:
P[0] = 0 Q[0] = 8
P[1] = 0 Q[1] = 2
P[2] = 4 Q[2] = 5
P[3] = 7 Q[3] = 7
The minimal nucleotides from these ranges are as follows:
(0, 8) is A identified by 1,
(0, 2) is A identified by 1,
(4, 5) is C identified by 2,
(7, 7) is T identified by 4.
Write a function:
class Solution { public int[] solution(String S, int[] P, int[] Q); }
that, given a non-empty zero-indexed string S consisting of N characters and two non-empty zero-indexed arrays P and Q consisting of M integers, returns an array consisting of M characters specifying the consecutive answers to all queries.
The sequence should be returned as:
a Results structure (in C), or
a vector of integers (in C++), or
a Results record (in Pascal), or
an array of integers (in any other programming language).
For example, given the string S = GACACCATA and arrays P, Q such that:
P[0] = 0 Q[0] = 8
P[1] = 0 Q[1] = 2
P[2] = 4 Q[2] = 5
P[3] = 7 Q[3] = 7
the function should return the values [1, 1, 2, 4], as explained above.
Assume that:
N is an integer within the range [1..100,000];
M is an integer within the range [1..50,000];
each element of array P, Q is an integer within the range [0..N − 1];
P[i] ≤ Q[i];
string S consists only of upper-case English letters A, C, G, T.
Complexity:
expected worst-case time complexity is O(N+M);
expected worst-case space complexity is O(N),
beyond input storage
(not counting the storage required for input arguments).
Elements of input arrays can be modified.
My solution is:
class Solution {
public int[] solution(String S, int[] P, int[] Q) {
final char c[] = S.toCharArray();
final int answer[] = new int[P.length];
int tempAnswer;
char tempC;
for (int iii = 0; iii < P.length; iii++) {
tempAnswer = 4;
for (int zzz = P[iii]; zzz <= Q[iii]; zzz++) {
tempC = c[zzz];
if (tempC == 'A') {
tempAnswer = 1;
break;
} else if (tempC == 'C') {
if (tempAnswer > 2) {
tempAnswer = 2;
}
} else if (tempC == 'G') {
if (tempAnswer > 3) {
tempAnswer = 3;
}
}
}
answer[iii] = tempAnswer;
}
return answer;
}
}
It is not optimal, I believe it's supposed to be done within one loop, any hint how can I achieve it?
You can check quality of your solution here https://codility.com/train/ test name is Genomic-range-query.
Here is the solution that got 100 out of 100 in codility.com. Please read about prefix sums to understand the solution:
public static int[] solveGenomicRange(String S, int[] P, int[] Q) {
//used jagged array to hold the prefix sums of each A, C and G genoms
//we don't need to get prefix sums of T, you will see why.
int[][] genoms = new int[3][S.length()+1];
//if the char is found in the index i, then we set it to be 1 else they are 0
//3 short values are needed for this reason
short a, c, g;
for (int i=0; i<S.length(); i++) {
a = 0; c = 0; g = 0;
if ('A' == (S.charAt(i))) {
a=1;
}
if ('C' == (S.charAt(i))) {
c=1;
}
if ('G' == (S.charAt(i))) {
g=1;
}
//here we calculate prefix sums. To learn what's prefix sums look at here https://codility.com/media/train/3-PrefixSums.pdf
genoms[0][i+1] = genoms[0][i] + a;
genoms[1][i+1] = genoms[1][i] + c;
genoms[2][i+1] = genoms[2][i] + g;
}
int[] result = new int[P.length];
//here we go through the provided P[] and Q[] arrays as intervals
for (int i=0; i<P.length; i++) {
int fromIndex = P[i];
//we need to add 1 to Q[i],
//because our genoms[0][0], genoms[1][0] and genoms[2][0]
//have 0 values by default, look above genoms[0][i+1] = genoms[0][i] + a;
int toIndex = Q[i]+1;
if (genoms[0][toIndex] - genoms[0][fromIndex] > 0) {
result[i] = 1;
} else if (genoms[1][toIndex] - genoms[1][fromIndex] > 0) {
result[i] = 2;
} else if (genoms[2][toIndex] - genoms[2][fromIndex] > 0) {
result[i] = 3;
} else {
result[i] = 4;
}
}
return result;
}
Simple, elegant, domain specific, 100/100 solution in JS with comments!
function solution(S, P, Q) {
var N = S.length, M = P.length;
// dictionary to map nucleotide to impact factor
var impact = {A : 1, C : 2, G : 3, T : 4};
// nucleotide total count in DNA
var currCounter = {A : 0, C : 0, G : 0, T : 0};
// how many times nucleotide repeats at the moment we reach S[i]
var counters = [];
// result
var minImpact = [];
var i;
// count nucleotides
for(i = 0; i <= N; i++) {
counters.push({A: currCounter.A, C: currCounter.C, G: currCounter.G});
currCounter[S[i]]++;
}
// for every query
for(i = 0; i < M; i++) {
var from = P[i], to = Q[i] + 1;
// compare count of A at the start of query with count at the end of equry
// if counter was changed then query contains A
if(counters[to].A - counters[from].A > 0) {
minImpact.push(impact.A);
}
// same things for C and others nucleotides with higher impact factor
else if(counters[to].C - counters[from].C > 0) {
minImpact.push(impact.C);
}
else if(counters[to].G - counters[from].G > 0) {
minImpact.push(impact.G);
}
else { // one of the counters MUST be changed, so its T
minImpact.push(impact.T);
}
}
return minImpact;
}
Java, 100/100, but with no cumulative/prefix sums! I stashed the last occurrence index of lower 3 nucelotides in a array "map". Later I check if the last index is between P-Q. If so it returns the nuclotide, if not found, it's the top one (T):
class Solution {
int[][] lastOccurrencesMap;
public int[] solution(String S, int[] P, int[] Q) {
int N = S.length();
int M = P.length;
int[] result = new int[M];
lastOccurrencesMap = new int[3][N];
int lastA = -1;
int lastC = -1;
int lastG = -1;
for (int i = 0; i < N; i++) {
char c = S.charAt(i);
if (c == 'A') {
lastA = i;
} else if (c == 'C') {
lastC = i;
} else if (c == 'G') {
lastG = i;
}
lastOccurrencesMap[0][i] = lastA;
lastOccurrencesMap[1][i] = lastC;
lastOccurrencesMap[2][i] = lastG;
}
for (int i = 0; i < M; i++) {
int startIndex = P[i];
int endIndex = Q[i];
int minimum = 4;
for (int n = 0; n < 3; n++) {
int lastOccurence = getLastNucleotideOccurrence(startIndex, endIndex, n);
if (lastOccurence != 0) {
minimum = n + 1;
break;
}
}
result[i] = minimum;
}
return result;
}
int getLastNucleotideOccurrence(int startIndex, int endIndex, int nucleotideIndex) {
int[] lastOccurrences = lastOccurrencesMap[nucleotideIndex];
int endValueLastOccurenceIndex = lastOccurrences[endIndex];
if (endValueLastOccurenceIndex >= startIndex) {
return nucleotideIndex + 1;
} else {
return 0;
}
}
}
Here is the solution, supposing someone is still interested.
class Solution {
public int[] solution(String S, int[] P, int[] Q) {
int[] answer = new int[P.length];
char[] chars = S.toCharArray();
int[][] cumulativeAnswers = new int[4][chars.length + 1];
for (int iii = 0; iii < chars.length; iii++) {
if (iii > 0) {
for (int zzz = 0; zzz < 4; zzz++) {
cumulativeAnswers[zzz][iii + 1] = cumulativeAnswers[zzz][iii];
}
}
switch (chars[iii]) {
case 'A':
cumulativeAnswers[0][iii + 1]++;
break;
case 'C':
cumulativeAnswers[1][iii + 1]++;
break;
case 'G':
cumulativeAnswers[2][iii + 1]++;
break;
case 'T':
cumulativeAnswers[3][iii + 1]++;
break;
}
}
for (int iii = 0; iii < P.length; iii++) {
for (int zzz = 0; zzz < 4; zzz++) {
if ((cumulativeAnswers[zzz][Q[iii] + 1] - cumulativeAnswers[zzz][P[iii]]) > 0) {
answer[iii] = zzz + 1;
break;
}
}
}
return answer;
}
}
In case anyone cares about C:
#include <string.h>
struct Results solution(char *S, int P[], int Q[], int M) {
int i, a, b, N, *pA, *pC, *pG;
struct Results result;
result.A = malloc(sizeof(int) * M);
result.M = M;
// calculate prefix sums
N = strlen(S);
pA = malloc(sizeof(int) * N);
pC = malloc(sizeof(int) * N);
pG = malloc(sizeof(int) * N);
pA[0] = S[0] == 'A' ? 1 : 0;
pC[0] = S[0] == 'C' ? 1 : 0;
pG[0] = S[0] == 'G' ? 1 : 0;
for (i = 1; i < N; i++) {
pA[i] = pA[i - 1] + (S[i] == 'A' ? 1 : 0);
pC[i] = pC[i - 1] + (S[i] == 'C' ? 1 : 0);
pG[i] = pG[i - 1] + (S[i] == 'G' ? 1 : 0);
}
for (i = 0; i < M; i++) {
a = P[i] - 1;
b = Q[i];
if ((pA[b] - pA[a]) > 0) {
result.A[i] = 1;
} else if ((pC[b] - pC[a]) > 0) {
result.A[i] = 2;
} else if ((pG[b] - pG[a]) > 0) {
result.A[i] = 3;
} else {
result.A[i] = 4;
}
}
return result;
}
Here is my solution Using Segment Tree O(n)+O(log n)+O(M) time
public class DNAseq {
public static void main(String[] args) {
String S="CAGCCTA";
int[] P={2, 5, 0};
int[] Q={4, 5, 6};
int [] results=solution(S,P,Q);
System.out.println(results[0]);
}
static class segmentNode{
int l;
int r;
int min;
segmentNode left;
segmentNode right;
}
public static segmentNode buildTree(int[] arr,int l,int r){
if(l==r){
segmentNode n=new segmentNode();
n.l=l;
n.r=r;
n.min=arr[l];
return n;
}
int mid=l+(r-l)/2;
segmentNode le=buildTree(arr,l,mid);
segmentNode re=buildTree(arr,mid+1,r);
segmentNode root=new segmentNode();
root.left=le;
root.right=re;
root.l=le.l;
root.r=re.r;
root.min=Math.min(le.min,re.min);
return root;
}
public static int getMin(segmentNode root,int l,int r){
if(root.l>r || root.r<l){
return Integer.MAX_VALUE;
}
if(root.l>=l&& root.r<=r) {
return root.min;
}
return Math.min(getMin(root.left,l,r),getMin(root.right,l,r));
}
public static int[] solution(String S, int[] P, int[] Q) {
int[] arr=new int[S.length()];
for(int i=0;i<S.length();i++){
switch (S.charAt(i)) {
case 'A':
arr[i]=1;
break;
case 'C':
arr[i]=2;
break;
case 'G':
arr[i]=3;
break;
case 'T':
arr[i]=4;
break;
default:
break;
}
}
segmentNode root=buildTree(arr,0,S.length()-1);
int[] result=new int[P.length];
for(int i=0;i<P.length;i++){
result[i]=getMin(root,P[i],Q[i]);
}
return result;
} }
Here is a C# solution, the basic idea is pretty much the same as the other answers, but it may be cleaner:
using System;
class Solution
{
public int[] solution(string S, int[] P, int[] Q)
{
int N = S.Length;
int M = P.Length;
char[] chars = {'A','C','G','T'};
//Calculate accumulates
int[,] accum = new int[3, N+1];
for (int i = 0; i <= 2; i++)
{
for (int j = 0; j < N; j++)
{
if(S[j] == chars[i]) accum[i, j+1] = accum[i, j] + 1;
else accum[i, j+1] = accum[i, j];
}
}
//Get minimal nucleotides for the given ranges
int diff;
int[] minimums = new int[M];
for (int i = 0; i < M; i++)
{
minimums[i] = 4;
for (int j = 0; j <= 2; j++)
{
diff = accum[j, Q[i]+1] - accum[j, P[i]];
if (diff > 0)
{
minimums[i] = j+1;
break;
}
}
}
return minimums;
}
}
Here is my solution. Got %100 . Of course I needed to first check and study a little bit prefix sums.
public int[] solution(String S, int[] P, int[] Q){
int[] result = new int[P.length];
int[] factor1 = new int[S.length()];
int[] factor2 = new int[S.length()];
int[] factor3 = new int[S.length()];
int[] factor4 = new int[S.length()];
int factor1Sum = 0;
int factor2Sum = 0;
int factor3Sum = 0;
int factor4Sum = 0;
for(int i=0; i<S.length(); i++){
switch (S.charAt(i)) {
case 'A':
factor1Sum++;
break;
case 'C':
factor2Sum++;
break;
case 'G':
factor3Sum++;
break;
case 'T':
factor4Sum++;
break;
default:
break;
}
factor1[i] = factor1Sum;
factor2[i] = factor2Sum;
factor3[i] = factor3Sum;
factor4[i] = factor4Sum;
}
for(int i=0; i<P.length; i++){
int start = P[i];
int end = Q[i];
if(start == 0){
if(factor1[end] > 0){
result[i] = 1;
}else if(factor2[end] > 0){
result[i] = 2;
}else if(factor3[end] > 0){
result[i] = 3;
}else{
result[i] = 4;
}
}else{
if(factor1[end] > factor1[start-1]){
result[i] = 1;
}else if(factor2[end] > factor2[start-1]){
result[i] = 2;
}else if(factor3[end] > factor3[start-1]){
result[i] = 3;
}else{
result[i] = 4;
}
}
}
return result;
}
If someone is still interested in this exercise, I share my Python solution (100/100 in Codility)
def solution(S, P, Q):
count = []
for i in range(3):
count.append([0]*(len(S)+1))
for index, i in enumerate(S):
count[0][index+1] = count[0][index] + ( i =='A')
count[1][index+1] = count[1][index] + ( i =='C')
count[2][index+1] = count[2][index] + ( i =='G')
result = []
for i in range(len(P)):
start = P[i]
end = Q[i]+1
if count[0][end] - count[0][start]:
result.append(1)
elif count[1][end] - count[1][start]:
result.append(2)
elif count[2][end] - count[2][start]:
result.append(3)
else:
result.append(4)
return result
This is my JavaScript solution that got 100% across the board on Codility:
function solution(S, P, Q) {
let total = [];
let min;
for (let i = 0; i < P.length; i++) {
const substring = S.slice(P[i], Q[i] + 1);
if (substring.includes('A')) {
min = 1;
} else if (substring.includes('C')) {
min = 2;
} else if (substring.includes('G')) {
min = 3;
} else if (substring.includes('T')) {
min = 4;
}
total.push(min);
}
return total;
}
import java.util.Arrays;
import java.util.HashMap;
class Solution {
static HashMap<Character, Integer > characterMapping = new HashMap<Character, Integer>(){{
put('A',1);
put('C',2);
put('G',3);
put('T',4);
}};
public static int minimum(int[] arr) {
if (arr.length ==1) return arr[0];
int smallestIndex = 0;
for (int index = 0; index<arr.length; index++) {
if (arr[index]<arr[smallestIndex]) smallestIndex=index;
}
return arr[smallestIndex];
}
public int[] solution(String S, int[] P, int[] Q) {
final char[] characterInput = S.toCharArray();
final int[] integerInput = new int[characterInput.length];
for(int counter=0; counter < characterInput.length; counter++) {
integerInput[counter] = characterMapping.get(characterInput[counter]);
}
int[] result = new int[P.length];
//assuming P and Q have the same length
for(int index =0; index<P.length; index++) {
if (P[index]==Q[index]) {
result[index] = integerInput[P[index]];
break;
}
final int[] subArray = Arrays.copyOfRange(integerInput, P[index], Q[index]+1);
final int minimumValue = minimum(subArray);
result[index]= minimumValue;
}
return result;
}
}
Here's 100% Scala solution:
def solution(S: String, P: Array[Int], Q: Array[Int]): Array[Int] = {
val resp = for(ind <- 0 to P.length-1) yield {
val sub= S.substring(P(ind),Q(ind)+1)
var factor = 4
if(sub.contains("A")) {factor=1}
else{
if(sub.contains("C")) {factor=2}
else{
if(sub.contains("G")) {factor=3}
}
}
factor
}
return resp.toArray
}
And performance: https://codility.com/demo/results/trainingEUR4XP-425/
Hope this helps.
public int[] solution(String S, int[] P, int[] K) {
// write your code in Java SE 8
char[] sc = S.toCharArray();
int[] A = new int[sc.length];
int[] G = new int[sc.length];
int[] C = new int[sc.length];
int prevA =-1,prevG=-1,prevC=-1;
for(int i=0;i<sc.length;i++){
if(sc[i]=='A')
prevA=i;
else if(sc[i] == 'G')
prevG=i;
else if(sc[i] =='C')
prevC=i;
A[i] = prevA;
G[i] = prevG;
C[i] = prevC;
//System.out.println(A[i]+ " "+G[i]+" "+C[i]);
}
int[] result = new int[P.length];
for(int i=0;i<P.length;i++){
//System.out.println(A[P[i]]+ " "+A[K[i]]+" "+C[P[i]]+" "+C[K[i]]+" "+P[i]+" "+K[i]);
if(A[K[i]] >=P[i] && A[K[i]] <=K[i]){
result[i] =1;
}
else if(C[K[i]] >=P[i] && C[K[i]] <=K[i]){
result[i] =2;
}else if(G[K[i]] >=P[i] && G[K[i]] <=K[i]){
result[i] =3;
}
else{
result[i]=4;
}
}
return result;
}
Python Solution with explanation
The idea is to hold an auxiliary array per nucleotide X, with position i (ignoring zero) is how many times X has occurred as of now. And so if we need the number of occurrences of X from position f to position t, we could take the following equation:
aux(t) - aux(f)
Time complexity is:
O(N+M)
def solution(S, P, Q):
n = len(S)
m = len(P)
aux = [[0 for i in range(n+1)] for i in [0,1,2]]
for i,c in enumerate(S):
aux[0][i+1] = aux[0][i] + ( c == 'A' )
aux[1][i+1] = aux[1][i] + ( c == 'C' )
aux[2][i+1] = aux[2][i] + ( c == 'G' )
result = []
for i in range(m):
fromIndex , toIndex = P[i] , Q[i] +1
if aux[0][toIndex] - aux[0][fromIndex] > 0:
r = 1
elif aux[1][toIndex] - aux[1][fromIndex] > 0:
r = 2
elif aux[2][toIndex] - aux[2][fromIndex] > 0:
r = 3
else:
r = 4
result.append(r)
return result
This is a Swift 4 solution to the same problem. It is based on #codebusta's solution above:
public func solution(_ S : inout String, _ P : inout [Int], _ Q : inout [Int]) -> [Int] {
var impacts = [Int]()
var prefixSum = [[Int]]()
for _ in 0..<3 {
let array = Array(repeating: 0, count: S.count + 1)
prefixSum.append(array)
}
for (index, character) in S.enumerated() {
var a = 0
var c = 0
var g = 0
switch character {
case "A":
a = 1
case "C":
c = 1
case "G":
g = 1
default:
break
}
prefixSum[0][index + 1] = prefixSum[0][index] + a
prefixSum[1][index + 1] = prefixSum[1][index] + c
prefixSum[2][index + 1] = prefixSum[2][index] + g
}
for tuple in zip(P, Q) {
if prefixSum[0][tuple.1 + 1] - prefixSum[0][tuple.0] > 0 {
impacts.append(1)
}
else if prefixSum[1][tuple.1 + 1] - prefixSum[1][tuple.0] > 0 {
impacts.append(2)
}
else if prefixSum[2][tuple.1 + 1] - prefixSum[2][tuple.0] > 0 {
impacts.append(3)
}
else {
impacts.append(4)
}
}
return impacts
}
Here is python solution with little explanation hope it helps some one.
Python codility 100%
def solution(S, P, Q):
"""
https://app.codility.com/demo/results/training8QBVFJ-EQB/
100%
Idea is consider solution as single dimensional array and use concept of prefix some ie.
stores the value in array for p,c and g based on frequency
array stores the frequency of p,c and g for all positions
Example -
# [0, 0, 1, 1, 1, 1, 1, 2] - prefix some of A - represents the max occurrence of A as 2 in array
# [0, 1, 1, 1, 2, 3, 3, 3] - prefix some of C - represents the max occurrence of A as 3 in array
# [0, 0, 0, 1, 1, 1, 1, 1] - prefix some of G - represents the max occurrence of A as 1 in array
# To find the query answers we can just use prefix some and find the distance between position
S = CAGCCTA
P[0] = 2 Q[0] = 4
P[1] = 5 Q[1] = 5
P[2] = 0 Q[2] = 6
Given a non-empty zero-indexed string S consisting of N characters and two non-empty zero-indexed arrays P and Q consisting
of M integers, returns an array consisting of M integers specifying the consecutive answers to all queries.
The part of the DNA between positions 2 and 4 contains nucleotide G and C (twice), whose impact factors are 3 and 2 respectively, so the answer is 2.
The part between positions 5 and 5 contains a single nucleotide T, whose impact factor is 4, so the answer is 4.
The part between positions 0 and 6 (the whole string) contains all nucleotide, in particular nucleotide A whose impact factor is 1, so the answer is 1.
N is an integer within the range [1..100,000];
M is an integer within the range [1..50,000];
each element of arrays P, Q is an integer within the range [0..N − 1];
P[K] ≤ Q[K], where 0 ≤ K < M;
string S consists only of upper-case English letters A, C, G, T.
Ref - https://github.com/ghanan94/codility-lesson-solutions/blob/master/Lesson%2005%20-%20Prefix%20Sums/PrefixSums.pdf
:return: return the values [2, 4, 1]
"""
# two d array - column size is 3 for a,c,g - not taking size 4 since that will be part of else ie. don`t need to calculate
# row size is the length of DNA sequence
prefix_sum_two_d_array = [[0 for i in range(len(S) + 1)] for j in range(3)]
# find the prefix some of all nucleotide in given sequence
for i, nucleotide in enumerate(S):
# store prefix some of each
# nucleotide == 'A -> 1 if true 0 if false
# [0, 0, 1, 1, 1, 1, 1, 2] - prefix some of A - represents the max occurrence of A as 2 in array
prefix_sum_two_d_array[0][i + 1] = prefix_sum_two_d_array[0][i] + (nucleotide == 'A')
# store prefix some of c
# [0, 1, 1, 1, 2, 3, 3, 3] - prefix some of C - represents the max occurrence of A as 3 in array
prefix_sum_two_d_array[1][i + 1] = prefix_sum_two_d_array[1][i] + (nucleotide == 'C')
# store prefix some of g
# [0, 0, 0, 1, 1, 1, 1, 1] - prefix some of G - represents the max occurrence of A as 1 in array
prefix_sum_two_d_array[2][i + 1] = prefix_sum_two_d_array[2][i] + (nucleotide == 'G')
#print(prefix_sum_two_d_array)
# now to find the query answers we can just use prefix some and find the distance between position
query_answers = []
for position in range(len(P)):
# for each query of p
# find the start index from p
start_index = P[position]
# find the end index from Q
end_index = Q[position] + 1
# find the value from prefix some array - just subtract end index and start index to find the value
if prefix_sum_two_d_array[0][end_index] - prefix_sum_two_d_array[0][start_index]:
query_answers.append(1)
elif prefix_sum_two_d_array[1][end_index] - prefix_sum_two_d_array[1][start_index]:
query_answers.append(2)
elif prefix_sum_two_d_array[2][end_index] - prefix_sum_two_d_array[2][start_index]:
query_answers.append(3)
else:
query_answers.append(4)
return query_answers
result = solution("CAGCCTA", [2, 5, 0], [4, 5, 6])
print("Sol " + str(result))
# Sol [2, 4, 1]
My 100% JavaScript solution with O(N + M) time complexity and no use of advanced built-in methods such as .includes, .substring, etc:
function solution(S, P, Q) {
// initialize prefix sums for A, C, G (you don't need T)
const A = [0];
const C = [0];
const G = [0];
// calculate prefix sums for A, C, G
for (let i = 0, len = S.length; i < len; i++) {
A.push(A[i] + Number("A" === S[i]));
C.push(C[i] + Number("C" === S[i]));
G.push(G[i] + Number("G" === S[i]));
}
// calculate the result using prefix sums
const result = [];
for (let i = 0, len = P.length; i < len; i++) {
const from = P[i];
const to = Q[i] + 1;
if (A[to] - A[from] > 0) {
result.push(1);
} else if (C[to] - C[from] > 0) {
result.push(2);
} else if (G[to] - G[from] > 0) {
result.push(3);
} else {
result.push(4); // this is why you don't need T
}
}
return result;
}
pshemek's solution constrains itself to the space complexity (O(N)) - even with the 2-d array and the answer array because a constant (4) is used for the 2-d array. That solution also fits in with the computational complexity - whereas mine is O (N^2) - though the actual computational complexity is much lower because it skips over entire ranges that include minimal values.
I gave it a try - but mine ends up using more space - but makes more intuitive sense to me (C#):
public static int[] solution(String S, int[] P, int[] Q)
{
const int MinValue = 1;
Dictionary<char, int> stringValueTable = new Dictionary<char,int>(){ {'A', 1}, {'C', 2}, {'G', 3}, {'T', 4} };
char[] inputArray = S.ToCharArray();
int[,] minRangeTable = new int[S.Length, S.Length]; // The meaning of this table is [x, y] where x is the start index and y is the end index and the value is the min range - if 0 then it is the min range (whatever that is)
for (int startIndex = 0; startIndex < S.Length; ++startIndex)
{
int currentMinValue = 4;
int minValueIndex = -1;
for (int endIndex = startIndex; (endIndex < S.Length) && (minValueIndex == -1); ++endIndex)
{
int currentValue = stringValueTable[inputArray[endIndex]];
if (currentValue < currentMinValue)
{
currentMinValue = currentValue;
if (currentMinValue == MinValue) // We can stop iterating - because anything with this index in its range will always be minimal
minValueIndex = endIndex;
else
minRangeTable[startIndex, endIndex] = currentValue;
}
else
minRangeTable[startIndex, endIndex] = currentValue;
}
if (minValueIndex != -1) // Skip over this index - since it is minimal
startIndex = minValueIndex; // We would have a "+ 1" here - but the "auto-increment" in the for statement will get us past this index
}
int[] result = new int[P.Length];
for (int outputIndex = 0; outputIndex < result.Length; ++outputIndex)
{
result[outputIndex] = minRangeTable[P[outputIndex], Q[outputIndex]];
if (result[outputIndex] == 0) // We could avoid this if we initialized our 2-d array with 1's
result[outputIndex] = 1;
}
return result;
}
In pshemek's answer - the "trick" in the second loop is simply that once you've determined you've found a range with the minimal value - you don't need to continue iterating. Not sure if that helps.
The php 100/100 solution:
function solution($S, $P, $Q) {
$S = str_split($S);
$len = count($S);
$lep = count($P);
$arr = array();
$result = array();
$clone = array_fill(0, 4, 0);
for($i = 0; $i < $len; $i++){
$arr[$i] = $clone;
switch($S[$i]){
case 'A':
$arr[$i][0] = 1;
break;
case 'C':
$arr[$i][1] = 1;
break;
case 'G':
$arr[$i][2] = 1;
break;
default:
$arr[$i][3] = 1;
break;
}
}
for($i = 1; $i < $len; $i++){
for($j = 0; $j < 4; $j++){
$arr[$i][$j] += $arr[$i - 1][$j];
}
}
for($i = 0; $i < $lep; $i++){
$x = $P[$i];
$y = $Q[$i];
for($a = 0; $a < 4; $a++){
$sub = 0;
if($x - 1 >= 0){
$sub = $arr[$x - 1][$a];
}
if($arr[$y][$a] - $sub > 0){
$result[$i] = $a + 1;
break;
}
}
}
return $result;
}
This program has got score 100 and performance wise has got an edge over other java codes listed above!
The code can be found here.
public class GenomicRange {
final int Index_A=0, Index_C=1, Index_G=2, Index_T=3;
final int A=1, C=2, G=3, T=4;
public static void main(String[] args) {
GenomicRange gen = new GenomicRange();
int[] M = gen.solution( "GACACCATA", new int[] { 0,0,4,7 } , new int[] { 8,2,5,7 } );
System.out.println(Arrays.toString(M));
}
public int[] solution(String S, int[] P, int[] Q) {
int[] M = new int[P.length];
char[] charArr = S.toCharArray();
int[][] occCount = new int[3][S.length()+1];
int charInd = getChar(charArr[0]);
if(charInd!=3) {
occCount[charInd][1]++;
}
for(int sInd=1; sInd<S.length(); sInd++) {
charInd = getChar(charArr[sInd]);
if(charInd!=3)
occCount[charInd][sInd+1]++;
occCount[Index_A][sInd+1]+=occCount[Index_A][sInd];
occCount[Index_C][sInd+1]+=occCount[Index_C][sInd];
occCount[Index_G][sInd+1]+=occCount[Index_G][sInd];
}
for(int i=0;i<P.length;i++) {
int a,c,g;
if(Q[i]+1>=occCount[0].length) continue;
a = occCount[Index_A][Q[i]+1] - occCount[Index_A][P[i]];
c = occCount[Index_C][Q[i]+1] - occCount[Index_C][P[i]];
g = occCount[Index_G][Q[i]+1] - occCount[Index_G][P[i]];
M[i] = a>0? A : c>0 ? C : g>0 ? G : T;
}
return M;
}
private int getChar(char c) {
return ((c=='A') ? Index_A : ((c=='C') ? Index_C : ((c=='G') ? Index_G : Index_T)));
}
}
Here's a simple javascript solution which got 100%.
function solution(S, P, Q) {
var A = [];
var C = [];
var G = [];
var T = [];
var result = [];
var i = 0;
S.split('').forEach(function(a) {
if (a === 'A') {
A.push(i);
} else if (a === 'C') {
C.push(i);
} else if (a === 'G') {
G.push(i);
} else {
T.push(i);
}
i++;
});
function hasNucl(typeArray, start, end) {
return typeArray.some(function(a) {
return a >= P[j] && a <= Q[j];
});
}
for(var j=0; j<P.length; j++) {
if (hasNucl(A, P[j], P[j])) {
result.push(1)
} else if (hasNucl(C, P[j], P[j])) {
result.push(2);
} else if (hasNucl(G, P[j], P[j])) {
result.push(3);
} else {
result.push(4);
}
}
return result;
}
perl 100/100 solution:
sub solution {
my ($S, $P, $Q)=#_; my #P=#$P; my #Q=#$Q;
my #_A = (0), #_C = (0), #_G = (0), #ret =();
foreach (split //, $S)
{
push #_A, $_A[-1] + ($_ eq 'A' ? 1 : 0);
push #_C, $_C[-1] + ($_ eq 'C' ? 1 : 0);
push #_G, $_G[-1] + ($_ eq 'G' ? 1 : 0);
}
foreach my $i (0..$#P)
{
my $from_index = $P[$i];
my $to_index = $Q[$i] + 1;
if ( $_A[$to_index] - $_A[$from_index] > 0 )
{
push #ret, 1;
next;
}
if ( $_C[$to_index] - $_C[$from_index] > 0 )
{
push #ret, 2;
next;
}
if ( $_G[$to_index] - $_G[$from_index] > 0 )
{
push #ret, 3;
next;
}
push #ret, 4
}
return #ret;
}
Java 100/100
class Solution {
public int[] solution(String S, int[] P, int[] Q) {
int qSize = Q.length;
int[] answers = new int[qSize];
char[] sequence = S.toCharArray();
int[][] occCount = new int[3][sequence.length+1];
int[] geneImpactMap = new int['G'+1];
geneImpactMap['A'] = 0;
geneImpactMap['C'] = 1;
geneImpactMap['G'] = 2;
if(sequence[0] != 'T') {
occCount[geneImpactMap[sequence[0]]][0]++;
}
for(int i = 0; i < sequence.length; i++) {
occCount[0][i+1] = occCount[0][i];
occCount[1][i+1] = occCount[1][i];
occCount[2][i+1] = occCount[2][i];
if(sequence[i] != 'T') {
occCount[geneImpactMap[sequence[i]]][i+1]++;
}
}
for(int j = 0; j < qSize; j++) {
for(int k = 0; k < 3; k++) {
if(occCount[k][Q[j]+1] - occCount[k][P[j]] > 0) {
answers[j] = k+1;
break;
}
answers[j] = 4;
}
}
return answers;
}
}
In ruby (100/100)
def interval_sum x,y,p
p[y+1] - p[x]
end
def solution(s,p,q)
#Hash of arrays with prefix sums
p_sums = {}
respuesta = []
%w(A C G T).each do |letter|
p_sums[letter] = Array.new s.size+1, 0
end
(0...s.size).each do |count|
%w(A C G T).each do |letter|
p_sums[letter][count+1] = p_sums[letter][count]
end if count > 0
case s[count]
when 'A'
p_sums['A'][count+1] += 1
when 'C'
p_sums['C'][count+1] += 1
when 'G'
p_sums['G'][count+1] += 1
when 'T'
p_sums['T'][count+1] += 1
end
end
(0...p.size).each do |count|
x = p[count]
y = q[count]
if interval_sum(x, y, p_sums['A']) > 0 then
respuesta << 1
next
end
if interval_sum(x, y, p_sums['C']) > 0 then
respuesta << 2
next
end
if interval_sum(x, y, p_sums['G']) > 0 then
respuesta << 3
next
end
if interval_sum(x, y, p_sums['T']) > 0 then
respuesta << 4
next
end
end
respuesta
end
simple php 100/100 solution
function solution($S, $P, $Q) {
$result = array();
for ($i = 0; $i < count($P); $i++) {
$from = $P[$i];
$to = $Q[$i];
$length = $from >= $to ? $from - $to + 1 : $to - $from + 1;
$new = substr($S, $from, $length);
if (strpos($new, 'A') !== false) {
$result[$i] = 1;
} else {
if (strpos($new, 'C') !== false) {
$result[$i] = 2;
} else {
if (strpos($new, 'G') !== false) {
$result[$i] = 3;
} else {
$result[$i] = 4;
}
}
}
}
return $result;
}
Here's my Java (100/100) Solution:
class Solution {
private ImpactFactorHolder[] mHolder;
private static final int A=0,C=1,G=2,T=3;
public int[] solution(String S, int[] P, int[] Q) {
mHolder = createImpactHolderArray(S);
int queriesLength = P.length;
int[] result = new int[queriesLength];
for (int i = 0; i < queriesLength; ++i ) {
int value = 0;
if( P[i] == Q[i]) {
value = lookupValueForIndex(S.charAt(P[i])) + 1;
} else {
value = calculateMinImpactFactor(P[i], Q[i]);
}
result[i] = value;
}
return result;
}
public int calculateMinImpactFactor(int P, int Q) {
int minImpactFactor = 3;
for (int nucleotide = A; nucleotide <= T; ++nucleotide ) {
int qValue = mHolder[nucleotide].mOcurrencesSum[Q];
int pValue = mHolder[nucleotide].mOcurrencesSum[P];
// handling special cases when the less value is assigned on the P index
if( P-1 >= 0 ) {
pValue = mHolder[nucleotide].mOcurrencesSum[P-1] == 0 ? 0 : pValue;
} else if ( P == 0 ) {
pValue = mHolder[nucleotide].mOcurrencesSum[P] == 1 ? 0 : pValue;
}
if ( qValue - pValue > 0) {
minImpactFactor = nucleotide;
break;
}
}
return minImpactFactor + 1;
}
public int lookupValueForIndex(char nucleotide) {
int value = 0;
switch (nucleotide) {
case 'A' :
value = A;
break;
case 'C' :
value = C;
break;
case 'G':
value = G;
break;
case 'T':
value = T;
break;
default:
break;
}
return value;
}
public ImpactFactorHolder[] createImpactHolderArray(String S) {
int length = S.length();
ImpactFactorHolder[] holder = new ImpactFactorHolder[4];
holder[A] = new ImpactFactorHolder(1,'A', length);
holder[C] = new ImpactFactorHolder(2,'C', length);
holder[G] = new ImpactFactorHolder(3,'G', length);
holder[T] = new ImpactFactorHolder(4,'T', length);
int i =0;
for(char c : S.toCharArray()) {
int nucleotide = lookupValueForIndex(c);
++holder[nucleotide].mAcum;
holder[nucleotide].mOcurrencesSum[i] = holder[nucleotide].mAcum;
holder[A].mOcurrencesSum[i] = holder[A].mAcum;
holder[C].mOcurrencesSum[i] = holder[C].mAcum;
holder[G].mOcurrencesSum[i] = holder[G].mAcum;
holder[T].mOcurrencesSum[i] = holder[T].mAcum;
++i;
}
return holder;
}
private static class ImpactFactorHolder {
public ImpactFactorHolder(int impactFactor, char nucleotide, int length) {
mImpactFactor = impactFactor;
mNucleotide = nucleotide;
mOcurrencesSum = new int[length];
mAcum = 0;
}
int mImpactFactor;
char mNucleotide;
int[] mOcurrencesSum;
int mAcum;
}
}
Link: https://codility.com/demo/results/demoJFB5EV-EG8/
I'm looking forward to implement a Segment Tree similar to #Abhishek Kumar solution
My C++ solution
vector<int> solution(string &S, vector<int> &P, vector<int> &Q) {
vector<int> impactCount_A(S.size()+1, 0);
vector<int> impactCount_C(S.size()+1, 0);
vector<int> impactCount_G(S.size()+1, 0);
int lastTotal_A = 0;
int lastTotal_C = 0;
int lastTotal_G = 0;
for (int i = (signed)S.size()-1; i >= 0; --i) {
switch(S[i]) {
case 'A':
++lastTotal_A;
break;
case 'C':
++lastTotal_C;
break;
case 'G':
++lastTotal_G;
break;
};
impactCount_A[i] = lastTotal_A;
impactCount_C[i] = lastTotal_C;
impactCount_G[i] = lastTotal_G;
}
vector<int> results(P.size(), 0);
for (int i = 0; i < P.size(); ++i) {
int pIndex = P[i];
int qIndex = Q[i];
int numA = impactCount_A[pIndex]-impactCount_A[qIndex+1];
int numC = impactCount_C[pIndex]-impactCount_C[qIndex+1];
int numG = impactCount_G[pIndex]-impactCount_G[qIndex+1];
if (numA > 0) {
results[i] = 1;
}
else if (numC > 0) {
results[i] = 2;
}
else if (numG > 0) {
results[i] = 3;
}
else {
results[i] = 4;
}
}
return results;
}
/* 100/100 solution C++.
Using prefix sums. Firstly converting chars to integer in nuc variable. Then in a bi-dimensional vector we account the occurrence in S of each nucleoside x in it's respective prefix_sum[s][x]. After we just have to find out the lower nucluoside that occurred in each interval K.
*/
.
vector solution(string &S, vector &P, vector &Q) {
int n=S.size();
int m=P.size();
vector<vector<int> > prefix_sum(n+1,vector<int>(4,0));
int nuc;
//prefix occurrence sum
for (int s=0;s<n; s++) {
nuc = S.at(s) == 'A' ? 1 : (S.at(s) == 'C' ? 2 : (S.at(s) == 'G' ? 3 : 4) );
for (int u=0;u<4;u++) {
prefix_sum[s+1][u] = prefix_sum[s][u] + ((u+1)==nuc?1:0);
}
}
//find minimal impact factor in each interval K
int lower_impact_factor;
for (int k=0;k<m;k++) {
lower_impact_factor=4;
for (int u=2;u>=0;u--) {
if (prefix_sum[Q[k]+1][u] - prefix_sum[P[k]][u] != 0)
lower_impact_factor = u+1;
}
P[k]=lower_impact_factor;
}
return P;
}
static public int[] solution(String S, int[] P, int[] Q) {
// write your code in Java SE 8
int A[] = new int[S.length() + 1], C[] = new int[S.length() + 1], G[] = new int[S.length() + 1];
int last_a = 0, last_c = 0, last_g = 0;
int results[] = new int[P.length];
int p = 0, q = 0;
for (int i = S.length() - 1; i >= 0; i -= 1) {
switch (S.charAt(i)) {
case 'A': {
last_a += 1;
break;
}
case 'C': {
last_c += 1;
break;
}
case 'G': {
last_g += 1;
break;
}
}
A[i] = last_a;
G[i] = last_g;
C[i] = last_c;
}
for (int i = 0; i < P.length; i++) {
p = P[i];
q = Q[i];
if (A[p] - A[q + 1] > 0) {
results[i] = 1;
} else if (C[p] - C[q + 1] > 0) {
results[i] = 2;
} else if (G[p] - G[q + 1] > 0) {
results[i] = 3;
} else {
results[i] = 4;
}
}
return results;
}
scala solution 100/100
import scala.annotation.switch
import scala.collection.mutable
object Solution {
def solution(s: String, p: Array[Int], q: Array[Int]): Array[Int] = {
val n = s.length
def arr = mutable.ArrayBuffer.fill(n + 1)(0L)
val a = arr
val c = arr
val g = arr
val t = arr
for (i <- 1 to n) {
def inc(z: mutable.ArrayBuffer[Long]): Unit = z(i) = z(i - 1) + 1L
def shift(z: mutable.ArrayBuffer[Long]): Unit = z(i) = z(i - 1)
val char = s(i - 1)
(char: #switch) match {
case 'A' => inc(a); shift(c); shift(g); shift(t);
case 'C' => shift(a); inc(c); shift(g); shift(t);
case 'G' => shift(a); shift(c); inc(g); shift(t);
case 'T' => shift(a); shift(c); shift(g); inc(t);
}
}
val r = mutable.ArrayBuffer.fill(p.length)(0)
for (i <- p.indices) {
val start = p(i)
val end = q(i) + 1
r(i) =
if (a(start) != a(end)) 1
else if (c(start) != c(end)) 2
else if (g(start) != g(end)) 3
else if (t(start) != t(end)) 4
else 0
}
r.toArray
}
}