AdSense

Showing posts with label Algorithm. Show all posts
Showing posts with label Algorithm. Show all posts

Thursday, March 26, 2015

Markov String Generator

Update 2015 - 03 - 31:

Ok, I optimized to code and removed those cumbersome extra space. Now my code reads the strings from the file and generate the table directly.


public class StringGenerator2 {
 private class Frequency{
  String s;//hashcode of the string
  int count;//occurrence
  public Frequency(String s, int c){
   this.s = s;
   this.count = c;
  }
 }
 Map> table;
 Random r;
 public StringGenerator2(String fileName) throws FileNotFoundException, IOException{
  table = new HashMap> ();
  r = new Random();
  generateTable(fileName);
  assert(isUniqueList());
 }
 public void generateTable(String fileName) throws FileNotFoundException, IOException{
  BufferedReader reader = new BufferedReader(new FileReader(fileName));
  String line;
  String last = null;
  while((line = reader.readLine()) != null){
   String[] row = line.split(" ");
   for(String s : row){
    String puntua = null;
    if(!Character.isLetter(s.charAt(s.length() - 1))){
     puntua = s.substring(s.length() - 1);
     s = s.substring(0, s.length() - 1);
    }
    if(!table.containsKey(s))
     table.put(s, new ArrayList ());
    if(last != null)
     add(table.get(last), s);
    if(puntua != null)
     add(table.get(s), puntua);
    last = s;
   }
  }
  reader.close();
  mapFrequency();
 }
 private void add(List next, String s){
  int index = -1;
  for(int i = 0; i < next.size(); i++){
   if(next.get(i).s.equals(s)){
    index = i;
    break;
   }
  }
  if(index != -1)
   next.get(index).count++;
  else
   next.add(new Frequency(s, 1));
 }
 private void mapFrequency(){
  for(Entry> e : table.entrySet()){
   List next = e.getValue();
   for(int i = 1; i < next.size(); i++){
    next.get(i).count += next.get(i - 1).count;
   }
  }
 }
 private boolean isUniqueList(){
  Set words = new HashSet ();
  for(List next : table.values()){
   words.clear();
   for(Frequency f : next){
    if(!words.add(f.s))
     return false;
   }
  }
  return true;
 }
 public void generator(String outputName, int length) throws IOException{
  if(table.size() == 0){
   System.out.println("No training set found!");
   return;
  }
  FileWriter writer = new FileWriter(outputName);
  int index = 0;
  String last = null;
  int countWord = 0;//number of words in one line
  while(index < length){
   String s = null;
   if(last == null || !table.containsKey(last)){
    //generate a random string from the key set
    Object[] keys = table.keySet().toArray();
    s = (String) keys[r.nextInt(keys.length)];
   } else
    s = getNext(table.get(last));
   writer.append(s).append(" ");
   countWord++;
   if(countWord == 15){
    writer.append("\n");
    countWord = 0;
   }
   last = s;
   index++;
  }
  writer.append(".\n");
  writer.flush();
  writer.close();
 }
 private String getNext(List next){
  int size = next.size();
  int nextIndex = r.nextInt(next.get(size - 1).count) + 1;
  for(Frequency f : next){
   if(nextIndex <= f.count)
    return f.s;
  }
  return next.get(r.nextInt(size)).s;
 }
  
}

Too much fun for this morning. I saw this problem posted on Career Cup yesterday. I couldn't understand what the problem really meant (see here), especially after I took a look at Wikipedia's couldn't-be-more-confusing explanation on Markov chain. But Google never let me down. I found this blog, which explained it in a clearer way. In short, using Markov chain to generate a new String is like using Bayesian  to predict when the first snow in next year will happen: based on the probability of when the first snow in last couple years happened.

In short, given a training set of strings, we create a table of the probability of all the next strings after a given string. Then when we need to generate a new string, we randomly select the first string from our training set, then pick the next string based on the probability of each "next" string in the table given the last generated string.

To get the next string based on the pre-calculated probability, I used this method. Basically when you get the probability distribution. You generate a random number, and based on the corresponding range in the PDF, you select the string.

I have to say that my implementation is not a good one. First, I used a list to store all strings into the memory, it definitely will cause a problem if there are too many strings. However, I don't know how to check if the current string is already in the table if I don't put everything into the memory.

Next, I calculate the frequency, to do that, I used another map, which is used to count the occurrence of each "next" string, then use two arrays to sum up, and finally use a list of structure (string, accumulated frequency) to store all "next" strings, given one string key in the table. See how much extra space I have used, this is definitely not good.

Moreover, when we hit a punctuation, I added the punctuation into the "next" list but didn't include it as a key.

To actually generate the string is easy, as I mentioned before, just keep randomly generate the next string in the "next" string list based on the frequency.

There are lots of ways to optimize this implementation. But I think this is enough for interview/learning purpose.


package markovChain;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
public class StringGenerator {
 List trainingSet;
 String file;
 public StringGenerator(String file) throws FileNotFoundException, IOException{
  this.file = file;
  trainingSet = new ArrayList ();
  read();
 }
 /**
  * read training set and buffered it into a list
  * @throws FileNotFoundException
  * @throws IOException
  */
 public void read() throws FileNotFoundException, IOException{
  BufferedReader reader = new BufferedReader(new FileReader(file));
  String line;
  while((line = reader.readLine()) != null){
   String[] row = line.split(" ");
   for(String s : row)
    trainingSet.add(s);
  }
  reader.close();  
 }
 /**
  * Generate frequency table
  * @param output
  * @param length
  * @throws IOException
  */
 private Map> generateTable(){
  Map> frequencyTable = new HashMap> ();
  frequencyTable.put(trainingSet.get(0), new ArrayList ());
  for(int i = 1; i < trainingSet.size(); i++){
   String s = trainingSet.get(i);
   String p = null;
   if(!Character.isLetter(s.charAt(s.length() - 1))){
    p = s.substring(s.length() - 1, s.length());
    s = s.substring(0, s.length() - 1);
   }
   if(!frequencyTable.containsKey(s)){
    frequencyTable.put(s, new ArrayList ());
    if(p != null)
     frequencyTable.get(s).add(p);
   }
   String last = trainingSet.get(i - 1);
   if(!Character.isLetter(last.charAt(last.length() - 1)))
    last = last.substring(0, last.length() - 1);
   frequencyTable.get(last).add(s); 
  }
  Map> nextFrequency = getFrequency(frequencyTable);
  return nextFrequency;
 }
 private Map> getFrequency(Map> frequencyTable){
  Map> countFre = new HashMap>();
  for(Entry> e : frequencyTable.entrySet()){
   String key = e.getKey();
   List next = e.getValue();
   Map f = new HashMap();
   for(String s : next){
    if(!f.containsKey(s))
     f.put(s, 1);
    else
     f.put(s, f.get(s) + 1);
   }
   List nextFrequency = mapFrequency(f);
   countFre.put(key, nextFrequency); 
  }
  return countFre;
 }
 private List mapFrequency(Map f){
  String[] array = new String[f.size()];
  int[] c = new int[f.size()];
  int index = 0;
  for(Entry ec : f.entrySet()){
   array[index] = ec.getKey();
   c[index] = ec.getValue();
   index++;
  }
  for(int i = 1; i < c.length; i++){
   c[i] += c[i - 1];
  }
  List rst = new ArrayList ();
  for(int i = 0; i < c.length; i++)
   rst.add(new Frequency(array[i], c[i]));
  return rst;
 }
 
 
 /**
  * generate the string
  * @param output
  * @param length
  * @throws IOException
  */
 public void Generator(String output, int length) throws IOException{
  if(trainingSet.size() == 0){
   System.out.println("No training set found!");
   return;
  }
  Map> nextFrequency = generateTable();
  Random r = new Random();
  FileWriter writer = new FileWriter(output);
  int i = 0;
  String last = null;
  int countWord = 0;
  while(i < length){
   String s = null;
   if(last == null || !nextFrequency.containsKey(last))
    s = trainingSet.get(r.nextInt(trainingSet.size()));
   else
    s = getNext(nextFrequency.get(last));
   writer.append(s).append(" ");
   countWord++;
   if(countWord == 15){
    writer.append("\n");
    countWord = 0;
   }
   last = s;
   i++;
  }
  writer.append(".\n");
  writer.flush();
  writer.close();
 }
 private String getNext(List nextFre){
  int size = nextFre.size();
  Random r = new Random();
  int next = r.nextInt(nextFre.get(size - 1).count) + 1;
  for(Frequency f : nextFre){
   if(next <= f.count)
    return f.s;
  }
  return nextFre.get(r.nextInt(size)).s;
 }
 /**
  * the structure that contains the string and its count
  * @author shirleyyoung
  *
  */
 private class Frequency{
  String s;
  int count;
  public Frequency(String s, int c){
   this.s = s;
   count = c;
  }
 } 
}


Test
In order to show my averseness to the research that I am working on. I decide to use some paragraphs from the papers I am reading as the test case.

"The dynamics of flexible polymers in shear is of great practical interest because this type of flow occurs whenever a fluid flows past a surface. 
Macroscopic, non-Newtonian rheological properties of polymer solutions, such
as flow-dependent viscosities and normal
stresses, result from microscopic stresses that
arise when polymeric molecules are stretched
and affect the solvent motion. Thus, much
effort has been directed at predicting the molecular
dynamics of polymers in shear flows. However, it has been difficult to rigorously
test these predictions because the dynamics
of a polymer molecule in shear have
not been observed directly. Experimental efforts
have mainly focused on measuring bulk
rheological properties or on measuring the
scattering of light or neutrons by polymer
solutions. Here we describe how single-molecule imaging techniques can be
used to study the configurations of polymers
in shear flow so that the detailed molecular predictions of theories can be tested.
In short, Shirley doesn't care about how polymer tumbles in shear flow!
Polymer dynamics are of central importance in materials science,
mechanical engineering, biology and medicine. The dynamics of
macromolecular solutions and melts in shear flow are typically
studied using bulk experimental methods such as light and
neutron scattering and birefringence. But the effect of shear
on the conformation and dynamics of individual polymers is
still not well understood. Here we describe observations of the
real-time dynamics of individual, flexible polymers fluorescently
labelled DNA molecules under a shear flow. The sheared
polymers exhibit many types of extended conformation with an
overall orientation ranging from parallel to perpendicular with
respect to the flow direction. For shear rates much smaller than
the inverse of the relaxation time of the molecule, the relative
populations of these two main types of conformation are
controlled by the rate of the shear flow. These results question
the adequacy of assumptions made in standard models of polymer
dynamics."

Here is the 300 strings generated from the input.

"mainly focused on measuring bulk rheological properties or on measuring the rate of extended conformation 
and dynamics of individual flexible polymers in shear is still not been difficult to rigorously 
test these predictions of shear on measuring bulk rheological properties or on measuring bulk rheological 
properties of assumptions made in shear is still not been difficult to rigorously test these 
two main types of polymer solutions Here we describe how polymer solutions , the detailed 
molecular dynamics of the molecular predictions of light and normal stresses result from microscopic stresses 
that the shear on measuring the molecular predictions of polymers exhibit many types of individual 
flexible polymers fluorescently labelled DNA molecules are controlled by the shear is of extended conformation 
with an overall orientation ranging from microscopic stresses result from microscopic stresses that arise when 
polymeric molecules are of polymers in shear flow Polymer dynamics of flow occurs whenever a 
shear flows However , by the molecule in shear rates much effort has been observed 
directly Experimental efforts have mainly focused on the effect of conformation with an overall orientation 
ranging from microscopic stresses that arise when polymeric molecules under a surface . flexible polymers 
exhibit many types of light and normal stresses , rheological properties of the adequacy of 
a shear flow so that the detailed molecular dynamics of conformation are of polymers in 
shear flow occurs whenever a surface . under a shear flow Polymer dynamics of assumptions 
made in shear flow are controlled by the dynamics of extended conformation and neutron scattering 
and affect the molecule the effect of great practical interest because the scattering and affect 
the scattering of polymers is still not been difficult to study the solvent motion Thus 
, much smaller than the inverse of shear flow Polymer dynamics of conformation and medicine."

Well, not that bad. 

Sunday, March 22, 2015

Sub-matrix with largest sum


Given an NxN matrix of positive and negative integers, write code to find the sub-matrix with the largest possible sum 

Cracking the coding interview provides an O(n^4) solution, which I really don't like. I was trying to use the maximum Subarray method, which was on the right track, but I missed a key point: I need to add up all previous subarrays.

The official name of calculating the maximum subarray is called the Kadane's algorithm. To extend it to get the maximum sub matrix in a 2D matrix, we first fix the left and right point (column index), then we calculate the accumulate sum from left to right for each row, and calculate the maximum subarray using Kadane's algorithm for the tmpSum array. Since we need to check all left and right bound, these two operations will take O(n^2), calculating the maximum subarray will take another O(n), so overall is O(n^3).


public class LargestSubMatrix {
 static int upperLeft = 0;
 static int upperRight = 0;
 static int length = 0;
 static int maxSum = Integer.MIN_VALUE;
 public static int largestArea(int[][] matrix){
  if(matrix == null || matrix.length == 0 || matrix[0].length == 0)
   return 0;
  int m = matrix.length;
  int n = matrix[0].length;
  int[] tmp = new int[m];
  for(int left = 0; left < n; left++){
   Arrays.fill(tmp, 0);
   for(int right = left; right < n; right++){
    for(int i = 0; i < m; i++)
     tmp[i] += matrix[i][right];
    int sum = maxSubArray(tmp);
    int tmpLength = length;
    if(sum > maxSum){
     maxSum = sum;
     upperLeft = left;
     upperRight = right;
    }
    else
     length = tmpLength;
   }
  }
  return maxSum;
 }
 private static int maxSubArray(int[] tmp){
  int tmpStart = 0;
  int sum = 0;
  int start = 0, finish = -1;
  int max = Integer.MIN_VALUE;
  for(int i = 0; i < tmp.length; i++){
   sum += tmp[i];
   if(sum < 0){
    sum = 0;
    tmpStart = i + 1;
   } else if(sum > max){
    max = sum;
    start = tmpStart;
    finish = i;
   }
  }
  if(finish != -1){
   if(max > maxSum)
    length = finish - start + 1;
   return max;
  }
  max = tmp[0];
  length = 1;
  for(int i = 1; i < tmp.length; i++)
   max = Math.max(max, tmp[i]);
  return max;
 }

 public static void main(String[] args) {
  int[][] matrix = new int[4][5];
  matrix[0][0] = 1;
  matrix[0][1] = 2;
  matrix[0][2] = -1;
  matrix[0][3] = -4;
  matrix[0][4] = -20;
  matrix[1][0] = -8;
  matrix[1][1] = -3;
  matrix[1][2] = 4;
  matrix[1][3] = 2;
  matrix[1][4] = 1;
  matrix[2][0] = 3;
  matrix[2][1] = 8;
  matrix[2][2] = 10;
  matrix[2][3] = 1;
  matrix[2][4] = 3;
  matrix[3][0] = -4;
  matrix[3][1] = -1;
  matrix[3][2] = 1;
  matrix[3][3] = 7;
  matrix[3][4] = -6;
  
  System.out.println(largestArea(matrix));
  System.out.println(upperLeft);
  System.out.println(upperRight);
  System.out.println(length);

 }

}

Sunday, January 18, 2015

The amazing maze

The idea came from a FB interview question: design a maze. So I googled, and found tremendous solutions. Mainly there are three ways to design a maze:

  • Depth-first search;
  • Randomized Kruskal's algorithm;
  • Randomized Prim's algorithm;
  • and so on.

See Wikipedia for more information.

The goal here is to design a "perfect" maze:




  • There are no cycles;
  • There is a unique path from the start cell in the maze to the end cell. 

Here I use randomized Kruskal's algorithm with a disjoint set data structure to perform union method.  It works in the following way:

  1. Create a list of all walls that potentially can be destroyed;
  2. Randomly choose a wall index;
  3. Union the two adjacent cells that are separated by the wall;
  4. Repeat until all cells are in the same set.


The union method acts like knocking down the wall, i.e., if two cells are in the same set, they are connected. When all cells are in the same set, there must be one path from the start cell to the end cell. Moreover, since every time we union two cells that are in different sets, there is no path between the cell before union, and since after the union, no other wall will be knocked down between these two cells, so there will be a unique path from any cell to another cell in the maze. Thus fulfill the "perfect" maze requirement.


public class Maze {
 private int[] grid;
 private int rows;
 private int columns;
 
 private Maze(int rows, int columns) {
  //using 1D array to represent cells
  //index / columns = row in the maze
  //index % columns = col in the maze
  this.grid = new int[rows * columns];
  //one cell is surrounded by walls in four directions
  //initially create cells with all walls up
  Arrays.fill(grid, UP | RIGHT | DOWN | LEFT);
  this.rows = rows;
  this.columns = columns;
 }
 
 private static final int UP = 1;
 private static final int RIGHT = 2;
 private static final int  DOWN = 4;
 private static final int LEFT = 8;
 
 public static Maze createRandomMaze(int rows, int columns) {
  Maze maze = new Maze(rows, columns);
  //create all walls that potentially can be broken
  List walls = new ArrayList();
  for (int row = 0; row < rows; row++) {
   for (int col = 0; col < columns; col++) {
    if (row > 0) 
     //cell = row * columns + col
     //cell / columns = row
     //cell % columns = col
     // represent the grid in the maze
     //the upper wall of the lower cell is the lower wall of the upper cell
     // the left wall of the right cell is the right wall of the left cell
     //so we only need to consider two directions
     walls.add(new Wall(row * columns + col, UP));
    if (col > 0)
     walls.add(new Wall(row * columns + col, LEFT));
   }
  }
  
  DisjointSet diset = new ArrayDisjointSet(rows * columns);
  //Object for generating random numbers
  Random rand = new Random();
  while (diset.size() > 1) {
   //get an index randomly
   int wallIndex = rand.nextInt(walls.size());
   int cell1 = walls.get(wallIndex).cell;
   int cell2 = (walls.get(wallIndex).direction == UP) ?
     cell1 - columns ://choose the cell and the one above it, break the upper wall
      cell1 - 1;//choose the one left to it, break the left wall
   //if there is no path between two cells
   //i.e., they are not in the same set
   if (diset.find(cell1) != diset.find(cell2)) {
    if (walls.get(wallIndex).direction == UP) {
     //break the upper wall of cell1 
     //which is also the lower wall of cells2
     maze.grid[cell1] ^= UP;
     maze.grid[cell2] ^= DOWN;
    }
    else {
     maze.grid[cell1] ^= LEFT;
     maze.grid[cell2] ^= RIGHT;
    }
    diset.union(cell1, cell2);
   }
   //the wall is knocked down, dead, disappeared, over...
   walls.remove(wallIndex);
  }
  return maze;
 }
 public static class Wall {
  private final int cell;
  private final int direction;
  public Wall(int cell, int direction) {
   this.cell = cell;
   this.direction = direction;
  }
 }
}

The result of a 30 by 30 grids:




The source code can be found on my Github: https://github.com/shirleyyoung0812/mazeDFS.git

To people who devote their lives to the dream they have. 

Friday, January 9, 2015

Divide Two Integers

So no multiplication, no division, nor mod. What is the only option? Bitwise operation!

For any integer, or long, "<< n" means multiply by 2 ^ n. And ">> n" means divide by 2 ^ n.


dividend = 45, divisor = 7
shift = 3, 7 * 2 * 2 * 2 = 56 > 45, the number of 7 that add up to be larger than 56 is 2 * 2 * 2 = 8
ans = 2 * 2 = 4,  a = 45 - 7 * 2 * 2= 17
shift = 2, 7 * 2 * 2 = 28 > 17,
ans = 4 + 2 = 6,  a = 17 - 7 * 2 = 3 < divisor

Note that since it is possible to overflow, we need to convert both dividend and divisor to long. ans should also be long type.


Update 2016-05-23:
45 ~ 7 * 6 = 7 * (2^2 + 2 ^ 1).

public int divide(int dividend, int divisor) {
        if (divisor == 0)
            return Integer.MAX_VALUE;
        boolean isNegative = (dividend > 0 && divisor < 0) ||
            (dividend < 0 && divisor > 0);
        long a = Math.abs((long)dividend);
        long b = Math.abs((long)divisor);
        if (a < b)
            return 0;
        long ans = 0;
        while (a >= b) {
            int shift = 0;
            while ((b << shift) <= a) 
                shift++;
            ans += ((long)1 << (shift - 1));
            a = a - (b << (shift - 1));
        }
        if (!isNegative && ans > (long)Integer.MAX_VALUE)
            return Integer.MAX_VALUE;
        return isNegative ? (int)-ans : (int)ans;
    }

Wednesday, December 24, 2014

The 100 Game

In "the 100 game," two players take turns adding, to a running 
total, any integer from 1..10. The player who first causes the running 
total to reach or exceed 100 wins. 
What if we change the game so that players cannot re-use integers? 
For example, if two players might take turns drawing from a common pool of numbers 
of 1..15 without replacement until they reach a total >= 100. This problem is 
to write a program that determines which player would win with ideal play. 

Write a procedure, "Boolean canIWin(int maxChoosableInteger, int desiredTotal)", 
which returns true if the first player to move can force a win with optimal play. 

Your priority should be programmer efficiency; don't focus on minimizing 
either space or time complexity. 
*/ 

Boolean canIWin(int maxChoosableInteger, int desiredTotal) { 
// Implementation here. Write yours 

}

This is a very interesting problem. Here is the heat discussion on Careercup. Overall, I don't think there is an optimal strategy, especially from the beginning of the game. So, just for fun, I include some randomness in to the game.

1. My code prints out all possible results of every start number from 1 to maxChoosableInteger assuming at the beginning of the game, there is no optimal strategy. 
2. There is no guarantee that starts from any number that P1 will win or P1 will lose. 
3. If the winnerNumber = (desiredTotal - currentSum) is in the range (1, maxChoosableInteger) and if  the winnerNumber hasn't been chosen, the current player will choose that number and she will win. 
4. If the winnerNumber has already been chosen, the current player will choose the smallest number available in order to prolong the game. 
5. If there is no winnerNumber, i.e., desiredTotal - currentSum isn't in the range (1, maxChoosableInteger), if the last player chose a small number (I classify "small" as number in the range (1, maxChoosableInteger/2)), the current player will choose a large number (range(maxChoosableInteger/2, maxChoosableInteger)), randomly (actually the assumption is not completely true). Otherwise she will choose a small number. Note if no such random number she can choose, she will choose the largest or smallest number available, respectively. 

I don't think my algorithm is the best one, some assumptions is not strict enough, but it's a little better than naive assumption of always choosing the largest number... I guess... 

Well, Merry Christmas. :)


import java.util.*;


public class The100Game {
 public void canIWin(int maxChooseableInteger, int desiredTotal) {
  if ((maxChooseableInteger*maxChooseableInteger + maxChooseableInteger) / 2 < desiredTotal) {
   throw new IllegalArgumentException("Wrong input!");
  }
  boolean[] pool = new boolean[maxChooseableInteger];
  for (int i = 0; i < maxChooseableInteger; i++) {
   System.out.println("If I start with: " + (i + 1));
   pool[i] = true;
   canP1Win(i + 1, i + 1, maxChooseableInteger, desiredTotal, pool, true);
    System.out.println("*******************"); 
   pool = new boolean[maxChooseableInteger];
  }
 }
 boolean canP1Win(int lastNumber, int currentSum, int maxChooseableInteger,
   int total, boolean[] pool, boolean isLastP1) {
  if (currentSum >= total) {
   if (isLastP1) 
    System.out.println("I can win!");
   else
    System.out.println("I will lose... :(");
   return isLastP1;
  }
  boolean isCurrentPlayerP1 = !isLastP1;
  int index;
  if (total - currentSum <= maxChooseableInteger) {
   index = maxChooseableInteger - 1;
   while (index >= 0 && pool[index]) {
    index--;
   }
   if (index < 0)
    return isCurrentPlayerP1;
   if ((currentSum + index + 1) >= total) {
    if (isCurrentPlayerP1) 
     System.out.println("I can win with: " + (index + 1));
    else
     System.out.println("I will lose with: " + (index + 1) + " :'(");
    return isCurrentPlayerP1;
   }
   //if the current player cannot win, she will choose a strategy not to let the opponent win
   index = 0;
   while (pool[index]) {
    index++;
   } 
  }
  else {
   Random r = new Random();
   if (lastNumber <= maxChooseableInteger / 2){
    index = r.nextInt(maxChooseableInteger / 2 + 1) + maxChooseableInteger/2;
    int count = 0;
    while ((pool[index] || index > maxChooseableInteger - 1 ) && count <= maxChooseableInteger) {
     index = r.nextInt(maxChooseableInteger / 2 + 1) + maxChooseableInteger/2;
     count++;
    }
    while (index >= 0 && pool[index]) {
     index--;
    }
    if (index < 0)
     return isCurrentPlayerP1;
   }
   else {
    index = r.nextInt(maxChooseableInteger / 2);
    int count = 0;
    while(pool[index] && count <= maxChooseableInteger) {
     index = r.nextInt(maxChooseableInteger / 2);
    }
    while (pool[index]) {
     index++;
    }
    //System.out.println("3 index: " + (index + 1));
   } 
  }
  if (isCurrentPlayerP1)
   System.out.println("I will choose " + (index + 1));
  else
   System.out.println("The oponent chooses: " + (index + 1));
  pool[index] = true;
  boolean p1CanWin = canP1Win(index + 1, currentSum + index + 1, maxChooseableInteger, total, 
    pool, isCurrentPlayerP1);
  if (isCurrentPlayerP1 && p1CanWin)
   return true;
  if (!isCurrentPlayerP1 && !p1CanWin)
   return false;
  pool[index] = false;
  return isCurrentPlayerP1 ? false : true;
 }
}

Longest Palindromic Substring - DP & O(n) solution

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

The first thought when seeing keyword "longest" and "substring" would be DP! Yep, and the solution is accepted.




public class LongestPalindrome {
    public String longestPalindrome(String s) {
     if (s == null || s.length() == 0)
      return "";
     String rst = s.substring(0,1);
     int maxSubstring = 1;
     boolean[][] palindrome = new boolean[s.length()][s.length()];
     for (int i = 0; i < s.length(); i++) {
      palindrome[i][i] = true;
     }
     for (int len = 1; len < s.length(); len++) {
      for (int i = 0; i + len < s.length() ; i++) {
       if (len < 2) {
        palindrome[i][i + len] = (s.charAt(i) == s.charAt(i + len));
       }
       else {
        palindrome[i][i + len] = palindrome[i + 1][i + len - 1] && (s.charAt(i) == s.charAt(i + len));
       }
       if (palindrome[i][i + len] && (len + 1 > maxSubstring)){
        maxSubstring = len + 1;
        rst = s.substring(i, i + len + 1);
       }
      }
     }
     return rst;
    }
}

However, as we know, 2D DP requires O(n^2) complexity. Naturally we will ask, can we do better?
Of course we can! Otherwise what's this post about?

The complete explanation of this O(n) solution, which is called, Manacher's Algorithm can be found here. I will just simplify it based on my understanding.

So consider we have a string s = "aabab". How can we check every substring using iteration? We need to check "aa", "aab", "aaba", "aabab", then "aba" ... and so on. Then this will be O(n^2), we are not doing anything better. But, what if we add something into the string:

# a # a # b # a # b #
0   1  2  3  4   5  6   7  8   9  10

Well, whatever symbol you would like to use is fine. The point is, now we double the length of the string, and every substring of s is symmetric in the new string. If we want to check "aa", it is symmetric against "#", "aba" is symmetric against "b". Thus, by iterate through the new string, we can check every substring of s in linear time.


public class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() == 0)
            return "";
        int maxSubstring = 1;
        String rst = s.substring(0, 1);
        for (int i = 1; i <= 2 * s.length() - 1; i++) {
            int count = 1;
            while (i - count >=  0 && i + count <= 2 * s.length() && get(s, i - count) == get(s, i + count)) {
                count++;
            }
            //Note that since "#" always equals "#", we will have an extra count for each substring
            count--;
            if (count > maxSubstring) {
                maxSubstring = count;
                rst = s.substring((i - count) / 2, (i + count) / 2);
            }
        }
        return rst;
    }
        private char get(String s, int index) {
            if (index % 2 == 0)
                return '#';
            else
                return s.charAt(index / 2);
        }
}

Ah, beautiful solution! :)