From 1c1f5dbd17467181b51075d6699e170900085589 Mon Sep 17 00:00:00 2001 From: En Yi Date: Fri, 30 Aug 2019 15:03:00 +0800 Subject: [PATCH] First Commit --- .gitignore | 5 + Week1/src/Outcast.java | 40 +++ Week1/src/SAP.java | 248 +++++++++++++++++++ Week1/src/WordNet.java | 151 ++++++++++++ Week2/src/SeamCarver.java | 381 +++++++++++++++++++++++++++++ Week3/src/BaseballElimination.java | 307 +++++++++++++++++++++++ Week4/src/BoggleBoard.java | 231 +++++++++++++++++ Week4/src/BoggleSolver.java | 315 ++++++++++++++++++++++++ Week5/src/BurrowsWheeler.java | 100 ++++++++ Week5/src/CircularSuffixArray.java | 117 +++++++++ Week5/src/MoveToFront.java | 76 ++++++ 11 files changed, 1971 insertions(+) create mode 100644 .gitignore create mode 100644 Week1/src/Outcast.java create mode 100644 Week1/src/SAP.java create mode 100644 Week1/src/WordNet.java create mode 100644 Week2/src/SeamCarver.java create mode 100644 Week3/src/BaseballElimination.java create mode 100644 Week4/src/BoggleBoard.java create mode 100644 Week4/src/BoggleSolver.java create mode 100644 Week5/src/BurrowsWheeler.java create mode 100644 Week5/src/CircularSuffixArray.java create mode 100644 Week5/src/MoveToFront.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29a2f67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +Week*/* +!.gitignore +!Week*/src/ +*.zip +*.jar diff --git a/Week1/src/Outcast.java b/Week1/src/Outcast.java new file mode 100644 index 0000000..9f23100 --- /dev/null +++ b/Week1/src/Outcast.java @@ -0,0 +1,40 @@ +import edu.princeton.cs.algs4.In; +import edu.princeton.cs.algs4.StdOut; + +public class Outcast { + private WordNet word_net; + // constructor takes a WordNet object + public Outcast(WordNet wordnet){ + word_net = wordnet; + } + // given an array of WordNet nouns, return an outcast + public String outcast(String[] nouns){ + // For each element + // Sum the distance with all elements + // Update if the current distance is maximum + int max_dist = -1; + String outcast = ""; + for(String noun : nouns){ + int dist = 0; + for (String noun2 : nouns){ + int d = word_net.distance(noun, noun2); + dist += d; + } + if (max_dist < 0 || dist > max_dist){ + max_dist = dist; + outcast = noun; + } + } + return outcast; + } + public static void main(String[] args){ + + WordNet wordnet = new WordNet(args[0], args[1]); + Outcast outcast = new Outcast(wordnet); + for (int t = 2; t < args.length; t++) { + In in = new In(args[t]); + String[] nouns = in.readAllStrings(); + StdOut.println(args[t] + ": " + outcast.outcast(nouns)); + } + } +} \ No newline at end of file diff --git a/Week1/src/SAP.java b/Week1/src/SAP.java new file mode 100644 index 0000000..780e679 --- /dev/null +++ b/Week1/src/SAP.java @@ -0,0 +1,248 @@ +import edu.princeton.cs.algs4.Digraph; +import edu.princeton.cs.algs4.Queue; +import edu.princeton.cs.algs4.In; +//import edu.princeton.cs.algs4.StdIn; +import edu.princeton.cs.algs4.StdOut; +import edu.princeton.cs.algs4.SET; + + +public class SAP { + private Digraph G; + + // constructor takes a digraph (not necessarily a DAG) + public SAP(Digraph G){ + // Make a copy of the digraph + this.G = new Digraph(G); + } + + // length of shortest ancestral path between v and w; -1 if no such path + public int length(int v, int w){ + // This is the more straightforward implementation + int[] dist = new int[1]; + ancestor(v, w, dist); + return dist[0]; + } + + // a common ancestor of v and w that participates in a shortest ancestral path; -1 if no such path + public int ancestor(int v, int w){ + return ancestor(v, w, new int[1]); + } + + // length of shortest ancestral path between any vertex in v and any vertex in w; -1 if no such path + public int length(Iterable v, Iterable w){ + if (v == null || w == null){ + throw new IllegalArgumentException("Null input detected"); + } + int[] shortest_dist = new int[]{-1}; + ancestor(v, w, shortest_dist); + return shortest_dist[0]; + } + + // a common ancestor that participates in shortest ancestral path; -1 if no such path + public int ancestor(Iterable v, Iterable w){ + if (v == null || w == null){ + throw new IllegalArgumentException("Null input detected"); + } + int[] shortest_dist = new int[]{-1}; + return ancestor(v, w, shortest_dist); + } + + private int ancestor(int v, int w, int[] distance){ + // This private ancestor function return sca but also + // return by reference the distance + + if (v<0 || w<0 || v>=G.V() || w>=G.V()) + throw new IllegalArgumentException("Input values does not exist in graph"); + + if (v==w){ + distance[0] = 0; + return v; + } + + int shortest_dist = -1; + int sca = -1; + + Queue node_queue = new Queue(); + + // First BFS + boolean[] firstMarked = new boolean[G.V()]; + int[] firstDist = new int[G.V()]; + node_queue.enqueue(v); + firstMarked[v] = true; + firstDist[v] = 0; + + while(!node_queue.isEmpty()){ + int a = node_queue.dequeue(); + for (int b: G.adj(a)){ + if (!firstMarked[b]){ + node_queue.enqueue(b); + firstMarked[b] = true; + firstDist[b] = firstDist[a] + 1; + } + } + } + + // Second BFS + boolean[] secondMarked = new boolean[G.V()]; + int[] secondDist = new int[G.V()]; + node_queue.enqueue(w); + secondMarked[w] = true; + secondDist[w] = 0; + + int current_dist = 0; + while(!node_queue.isEmpty()){ + int a = node_queue.dequeue(); + // Check for the distance to the common ancestor + // if reachable by the first one + if (firstMarked[a]){ + current_dist = firstDist[a] + secondDist[a]; + if (shortest_dist<0 || current_dist v, Iterable w, int[] distance){ + /* This is a variant of the one-to-one version of ancestor + It is redundant to do BFS on each pair of integer + Rather, when given one-to-many + The amount of BFS should only be done once on each vertices + + For sizes M and N (M being the minimum), Worst Case Growth rate: + Counting and Checking: M+N + First BFS: M * (E+V) + Second BFS: N *((E+V) + V*M) (Since everytime you explore a new node, you iterate across M) + Total: M + N + M * (E+V) + N * (E+V + V*M) + Or: (M+N)(E+V+1) + M*N*V + Compare to pairing: M*N*(2*(E+V)) => O(M*N*(E+V)) + + Extra memory used: M*V + */ + + // Counting and checking + int vSize = 0; + int wSize = 0; + for (Integer a : v){ + if (a==null) + throw new IllegalArgumentException("Null value detected"); + if (a<0 || a>=G.V()) + throw new IllegalArgumentException("Input values does not exist in graph"); + vSize++; + } + for (Integer a : w){ + if (a==null) + throw new IllegalArgumentException("Null value detected"); + if (a<0 || a>=G.V()) + throw new IllegalArgumentException("Input values does not exist in graph"); + wSize++; + } + // Determine which BFS to do first. Do it on the smaller set for lower memory + Iterable first, second; + if (wSize < vSize){ + first = w; + second = v; + }else{ + first = v; + second = w; + } + int firstSize = Math.min(vSize, wSize); + + + int shortest_dist = -1; + int sca = -1; + int i = 0; + Queue node_queue = new Queue(); + + // First BFS on all first + boolean[][] firstMarked = new boolean[firstSize][G.V()]; + int[][] firstDist = new int[firstSize][G.V()]; + for(int start : first){ + node_queue.enqueue(start); + firstMarked[i][start] = true; + firstDist[i][start] = 0; + + while(!node_queue.isEmpty()){ + int a = node_queue.dequeue(); + for (int b: G.adj(a)){ + if (!firstMarked[i][b]){ + node_queue.enqueue(b); + firstMarked[i][b] = true; + firstDist[i][b] = firstDist[i][a] + 1; + } + } + } + i++; + } + + // Second BFS for all second + boolean[] secondMarked; + int[] secondDist; + + for(int start : second){ + secondMarked = new boolean[G.V()]; + secondDist = new int[G.V()]; + node_queue.enqueue(start); + secondMarked[start] = true; + secondDist[start] = 0; + + int current_dist = 0; + while(!node_queue.isEmpty()){ + int a = node_queue.dequeue(); + // Check for the distance to the common ancestor + // if reachable by the first one + // For every node in the first + for(i=0;i v = new SET(); + v.add(1); + v.add(6); + SET w = new SET(); + w.add(3); + int length = sap.length(v, w); + int ancestor = sap.ancestor(v, w); + StdOut.printf("length = %d, ancestor = %d\n", length, ancestor); + //} + } + } + + \ No newline at end of file diff --git a/Week1/src/WordNet.java b/Week1/src/WordNet.java new file mode 100644 index 0000000..01cb815 --- /dev/null +++ b/Week1/src/WordNet.java @@ -0,0 +1,151 @@ +import edu.princeton.cs.algs4.Digraph; +import edu.princeton.cs.algs4.In; +import edu.princeton.cs.algs4.RedBlackBST; +import edu.princeton.cs.algs4.Bag; +import edu.princeton.cs.algs4.Topological; + +public class WordNet { + + private RedBlackBST> nouns_BST; + + private String[] sysnet_array; + private Digraph word_graph; + private int n_synset; + private SAP sapFinder; + + // constructor takes the name of the two input files + public WordNet(String synsets, String hypernyms){ + if (synsets == null || hypernyms == null){ + throw new IllegalArgumentException("Null argument(s) detected."); + } + + // Create a noun self balancing BST for gurantee log n serach + // Key of BST is the string, while the value is the Bag of synset id which the string belongs to + + // Get all the lines + In in = new In(synsets); + String[] lines = in.readAllLines(); + in.close(); + + n_synset = lines.length; + nouns_BST = new RedBlackBST>(); + sysnet_array = new String[n_synset]; + + int i = 0; + for(String line:lines) { + String[] parts = line.split(","); + + sysnet_array[i] = parts[1]; + + // Split the synset to get the noun, put the noun and id in BST + String[] nouns = parts[1].split(" "); + for(String noun : nouns){ + if (nouns_BST.contains(noun)){ + // If noun is already in BST + // Get its id Bag and add the current id + // This is fine since Bag is mutable + nouns_BST.get(noun).add(i); + }else{ + Bag bag = new Bag(); + bag.add(i); + nouns_BST.put(noun, bag); + } + } + i++; + } + + // Create the Digraph from the hypernyms + word_graph = new Digraph(n_synset); + in = new In(hypernyms); + while (!in.isEmpty()) { + String line = in.readLine(); + String[] parts = line.split(","); + int from = Integer.parseInt(parts[0]); + + for(int j=1;j nouns(){ + return nouns_BST.keys(); + } + + // is the word a WordNet noun? + public boolean isNoun(String word){ + if (word == null){ + throw new IllegalArgumentException("Word is null"); + } + return nouns_BST.contains(word); + } + + // distance between nounA and nounB (defined below) + public int distance(String nounA, String nounB){ + if (nounA == null || nounB == null){ + throw new IllegalArgumentException("Null input(s) detected."); + } + + if (!isNoun(nounA) || !isNoun(nounB) ) + throw new IllegalArgumentException("Input is not in WordNet"); + + // This is fine + if (nounA.equals(nounB)){ + return 0; + } + + // Because it is a rooted DAG, one common ancestor is always the root + return sapFinder.length(nouns_BST.get(nounA), nouns_BST.get(nounB)); + } + + // a synset (second field of synsets.txt) that is the common ancestor of nounA and nounB + // in a shortest ancestral path (defined below) + public String sap(String nounA, String nounB){ + if (nounA == null || nounB == null){ + throw new IllegalArgumentException("Null input(s) detected."); + } + + if (!isNoun(nounA) || !isNoun(nounB) ) + throw new IllegalArgumentException("Input is not in WordNet"); + + return sysnet_array[sapFinder.ancestor(nouns_BST.get(nounA), nouns_BST.get(nounB))]; + } + + + public static void main(String[] args){ + WordNet word_net = new WordNet("synsets.txt", "hypernyms.txt"); + /*Iterable nouns = word_net.nouns(); + for (String word : nouns){ + System.out.println(word); + }*/ + System.out.println(word_net.isNoun("ASCII")); + System.out.println(word_net.isNoun("orange_juice")); + System.out.println(word_net.isNoun("thrush")); + System.out.println(word_net.distance("do-si-do", "contredanse")); + System.out.println(word_net.sap("do-si-do", "contredanse")); + } + } \ No newline at end of file diff --git a/Week2/src/SeamCarver.java b/Week2/src/SeamCarver.java new file mode 100644 index 0000000..d3ddfab --- /dev/null +++ b/Week2/src/SeamCarver.java @@ -0,0 +1,381 @@ +import edu.princeton.cs.algs4.Picture; +import edu.princeton.cs.algs4.Stack; + +public class SeamCarver { + private Picture current_pic; + private int width; + private int height; + private final int R_BITMASK = 0xFF0000; + private final int G_BITMASK = 0x00FF00; + private final int B_BITMASK = 0x0000FF; + + private double[][] energies; + + private class HorizontalSeamSearcher{ + // Expects Picture Graph is DAG + // Use Acyclic SP procedures + public double[] distTo; // distTo[v] = distance of shortest s->v path + public int[] edgeTo; // edgeTo[v] = last edge on shortest s->v path + + public HorizontalSeamSearcher() { + int V = width * height + 2; + distTo = new double[V]; + edgeTo = new int[V]; + + for (int v = 0; v < V; v++) + distTo[v] = Double.POSITIVE_INFINITY; + distTo[0] = 0.0; + + for (int i=0;i 0){ + relax(v, v+1-width); + } + if (row < height - 1){ + relax(v, v+1+width); + } + }else if (col == width -1){ + relax(v, V-1); + } + } + } + } + // relax edge e + private void relax(int v, int w) { + double weight = 0; + if (w != width* height + 1){ + int row = (w-1) / width; + int col = (w-1) % width; + weight = energies[row][col]; + } + + if (distTo[w] > distTo[v] + weight) { + distTo[w] = distTo[v] + weight; + edgeTo[w] = v; + } + } + public Iterable getSeam() { + int v = edgeTo.length - 1; + Stack path = new Stack(); + for (int e = edgeTo[v]; e != 0; e = edgeTo[e]) { + path.push(e); + } + return path; + } + } + + private class VerticalSeamSearcher{ + // Expects Picture Graph is DAG + // Use Acyclic SP procedures + public double[] distTo; // distTo[v] = distance of shortest s->v path + public int[] edgeTo; // edgeTo[v] = last edge on shortest s->v path + + public VerticalSeamSearcher() { + int V = width * height + 2; + distTo = new double[V]; + edgeTo = new int[V]; + + for (int v = 0; v < V; v++) + distTo[v] = Double.POSITIVE_INFINITY; + distTo[0] = 0.0; + + // Topological sort should always have 0 as the first index + /*int[] topological = G.topologicalSort(); + // Topological sort should always have "end" as the last index + assert topological[topological.length-1] == G.V-1; + + }*/ + for (int v=0; v< V; v++) { + //for (PictureEdge e : G.adj(v)) + int row = (v-1) / width; + if (v==0){ + for (int i=1;i<=width;i++) + relax(v, i); + } + else if (row< height -1){ + relax(v, v+width); + int col = (v-1) % width; + if (col > 0){ + relax(v, v+width-1); + } + if (col < width - 1){ + relax(v, v+width+1); + } + }else if (row == height -1){ + relax(v, V-1); + } + } + } + // relax edge e + private void relax(int v, int w) { + double weight = 0; + if (w != width* height + 1){ + int row = (w-1) / width; + int col = (w-1) % width; + weight = energies[row][col]; + } + + if (distTo[w] > distTo[v] + weight) { + distTo[w] = distTo[v] + weight; + edgeTo[w] = v; + } + } + public Iterable getSeam() { + int v = edgeTo.length - 1; + Stack path = new Stack(); + for (int e = edgeTo[v]; e != 0; e = edgeTo[e]) { + path.push(e); + } + return path; + } + } + + // create a seam carver object based on the given picture + public SeamCarver(Picture picture){ + if (picture==null){ + throw new IllegalArgumentException("Null input"); + } + // Save a copy of the picture + current_pic = new Picture(picture); + // Get the dimensions + width = picture.width(); + height = picture.height(); + + // Init energy array and border values + energies = new double[height][width]; + for(int i=0;i=width || y <0 || y>=height){ + throw new IllegalArgumentException("Input out of range"); + } + return energies[y][x]; + } + + // sequence of indices for horizontal seam + public int[] findHorizontalSeam(){ + // Run SeamSearcher on the horizontal graph + HorizontalSeamSearcher seam = new HorizontalSeamSearcher(); + + int[] path = new int[width]; + int i=0; + for (Integer node : seam.getSeam()){ + path[i++] = (node-1) / width ; + if (i==width) + break; + } + return path; + } + + // sequence of indices for vertical seam + public int[] findVerticalSeam(){ + // Run SeamSearcher on the vertical graph + VerticalSeamSearcher seam = new VerticalSeamSearcher(); + int[] path = new int[height]; + int i=0; + for (int node : seam.getSeam()){ + path[i++] = (node-1) % width; + if (i==height) + break; + } + return path; + } + + private void check_seam(int[] seam){ + int prev_ind = seam[0]; + for(int i=1;i 1) + throw new IllegalArgumentException("Seam has a non adjacent index"); + prev_ind = seam[i]; + } + } + + // remove horizontal seam from current picture + public void removeHorizontalSeam(int[] seam){ + if (seam==null){ + throw new IllegalArgumentException("Null input"); + } + if (seam.length != width){ + throw new IllegalArgumentException("seam not equal to width"); + } + if (height <= 1){ + throw new IllegalArgumentException("Cannot carve pic any further"); + } + check_seam(seam); + + // Create a new Picture with the current dimensions + Picture newPicture = new Picture(width, height-1); + // Fill in the Picture except the carved pixels + // Resize the energies + for(int col=0;col>16) - ((nextx_RGB & R_BITMASK)>>16); + double Gx = ((prevx_RGB & G_BITMASK)>>8) - ((nextx_RGB & G_BITMASK)>>8); + double Bx = (prevx_RGB & B_BITMASK) - (nextx_RGB & B_BITMASK); + double deltax2 = Math.pow(Rx, 2) + Math.pow(Gx, 2) + Math.pow(Bx, 2); + + + int prevy_RGB = current_pic.getRGB(col, row-1); + int nexty_RGB = current_pic.getRGB(col, row+1); + double Ry = ((prevy_RGB & R_BITMASK)>>16) - ((nexty_RGB & R_BITMASK)>>16); + double Gy = ((prevy_RGB & G_BITMASK)>>8) - ((nexty_RGB & G_BITMASK)>>8); + double By = (prevy_RGB & B_BITMASK) - (nexty_RGB & B_BITMASK); + double deltay2 = Math.pow(Ry, 2) + Math.pow(Gy, 2) + Math.pow(By, 2); + energies[row][col] = Math.sqrt(deltax2 + deltay2); + } + + + + // unit testing (optional) + public static void main(String[] args){ + Picture pic = new Picture("cherrim.png"); + + SeamCarver carver = new SeamCarver(pic); + System.out.println(carver.width()); + System.out.println(carver.height()); + int n_vcuts = carver.width()/4; + int n_hcuts = carver.height()/4; + int[] seam; + for (int i=0;i teamNames; + private final String[] teamNamesind; + private final int[][] teamStats; + private final int[][] matches; + // create a baseball division from given filename in format specified below + public BaseballElimination(String filename){ + In in = new In(filename); + n_teams = in.readInt(); + + teamNames = new BST(); + teamNamesind = new String[n_teams]; + teamStats = new int[n_teams][3]; + matches = new int[n_teams][n_teams]; + + for(int i=0;i teams(){ + return teamNames.keys(); + } + // number of wins for given team + public int wins(String team){ + Integer n = teamNames.get(team); + if(n == null){ + throw new IllegalArgumentException("Team not found"); + } + return teamStats[n][0]; + } + // number of losses for given team + public int losses(String team) { + Integer n = teamNames.get(team); + if(n == null){ + throw new IllegalArgumentException("Team not found"); + } + return teamStats[n][1]; + } + // number of remaining games for given team + public int remaining(String team){ + Integer n = teamNames.get(team); + if(n == null){ + throw new IllegalArgumentException("Team not found"); + } + return teamStats[n][2]; + } + // number of remaining games between team1 and team2 + public int against(String team1, String team2) { + Integer m = teamNames.get(team1); + Integer n = teamNames.get(team2); + if(m == null){ + throw new IllegalArgumentException("Team 1 not found"); + } + if(n == null){ + throw new IllegalArgumentException("Team 2 not found"); + } + return matches[m][n]; + } + // is given team eliminated? + public boolean isEliminated(String team) { + Integer ind = teamNames.get(team); + if(ind == null){ + throw new IllegalArgumentException("Team not found"); + } + + int possible_wins = teamStats[ind][0] + teamStats[ind][2]; + //Trivial check + for(int i=0;i certificateOfElimination(String team) { + Integer ind = teamNames.get(team); + if(ind == null){ + throw new IllegalArgumentException("Team not found"); + } + + + int possible_wins = teamStats[ind][0] + teamStats[ind][2]; + //Trivial check + String[] team_set = new String[n_teams]; + int n=0,v=0; + for(int i=0;i0) + return new teamIterable(team_set, n); + + FlowEdge e; + int n_nodes = n_teams + 1 + (n_teams - 1) * (n_teams -2) / 2; + FlowNetwork elim_network = new FlowNetwork(n_nodes); + + // Init 0 to n_teams-2 as team node + // Init 0.5 * n_teams(n_teams-1) as the reamining matches + // Connect each match to the respective teams + n=0; + for(int i=0;i{ + String[] names; + + public teamIterable(String[] names, int length){ + this.names = new String[length]; + for(int i=0;i iterator(){ + return new teamIterator(); + } + + private class teamIterator implements Iterator{ + int current = 0; + + public boolean hasNext(){ + return current < names.length; + } + public String next(){ + return names[current++]; + } + } + + } + + public static void main(String[] args){ + BaseballElimination bball = new BaseballElimination("teams4a.txt"); + + int N = bball.numberOfTeams(); + System.out.println(N); + for(String s: bball.teams()){ + System.out.print(s); + System.out.print(" "); + System.out.print(bball.wins(s)); + System.out.print(" "); + System.out.print(bball.losses(s)); + System.out.print(" "); + System.out.print(bball.remaining(s)); + System.out.print(" "); + for(String t: bball.teams()){ + System.out.print(bball.against(s, t)); + System.out.print(" "); + } + + Iterable elim = bball.certificateOfElimination(s); + //System.out.print(elim); + if (elim != null){ + System.out.print(" Eliminated by: "); + for(String t: bball.certificateOfElimination(s)){ + System.out.print(t); + System.out.print(" "); + } + } + System.out.println(""); + } + + } + +} + diff --git a/Week4/src/BoggleBoard.java b/Week4/src/BoggleBoard.java new file mode 100644 index 0000000..d590ba6 --- /dev/null +++ b/Week4/src/BoggleBoard.java @@ -0,0 +1,231 @@ +/****************************************************************************** + * Compilation: javac BoggleBoard.java + * Execution: java BoggleBoard + * Dependencies: StdRandom.java In.java StdOut.java + * + * A data type for Boggle boards. + * + ******************************************************************************/ + +import edu.princeton.cs.algs4.In; +import edu.princeton.cs.algs4.StdOut; +import edu.princeton.cs.algs4.StdRandom; + +public class BoggleBoard { + // the 16 Boggle dice (1992 version) + private static final String[] BOGGLE_1992 = { + "LRYTTE", "VTHRWE", "EGHWNE", "SEOTIS", + "ANAEEG", "IDSYTT", "OATTOW", "MTOICU", + "AFPKFS", "XLDERI", "HCPOAS", "ENSIEU", + "YLDEVR", "ZNRNHL", "NMIQHU", "OBBAOJ" + }; + + // the 16 Boggle dice (1983 version) + private static final String[] BOGGLE_1983 = { + "AACIOT", "ABILTY", "ABJMOQ", "ACDEMP", + "ACELRS", "ADENVZ", "AHMORS", "BIFORX", + "DENOSW", "DKNOTU", "EEFHIY", "EGINTV", + "EGKLUY", "EHINPS", "ELPSTU", "GILRUW", + }; + + // the 25 Boggle Master / Boggle Deluxe dice + private static final String[] BOGGLE_MASTER = { + "AAAFRS", "AAEEEE", "AAFIRS", "ADENNN", "AEEEEM", + "AEEGMU", "AEGMNN", "AFIRSY", "BJKQXZ", "CCNSTW", + "CEIILT", "CEILPT", "CEIPST", "DDLNOR", "DHHLOR", + "DHHNOT", "DHLNOR", "EIIITT", "EMOTTT", "ENSSSU", + "FIPRSY", "GORRVW", "HIPRRY", "NOOTUW", "OOOTTU" + }; + + // the 25 Big Boggle dice + private static final String[] BOGGLE_BIG = { + "AAAFRS", "AAEEEE", "AAFIRS", "ADENNN", "AEEEEM", + "AEEGMU", "AEGMNN", "AFIRSY", "BJKQXZ", "CCENST", + "CEIILT", "CEILPT", "CEIPST", "DDHNOT", "DHHLOR", + "DHLNOR", "DHLNOR", "EIIITT", "EMOTTT", "ENSSSU", + "FIPRSY", "GORRVW", "IPRRRY", "NOOTUW", "OOOTTU" + }; + + + // letters and frequencies of letters in the English alphabet + private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final double[] FREQUENCIES = { + 0.08167, 0.01492, 0.02782, 0.04253, 0.12703, 0.02228, + 0.02015, 0.06094, 0.06966, 0.00153, 0.00772, 0.04025, + 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, + 0.06327, 0.09056, 0.02758, 0.00978, 0.02360, 0.00150, + 0.01974, 0.00074 + }; + + private final int m; // number of rows + private final int n; // number of columns + private char[][] board; // the m-by-n array of characters + + /** + * Initializes a random 4-by-4 board, by rolling the Hasbro dice. + */ + public BoggleBoard() { + m = 4; + n = 4; + StdRandom.shuffle(BOGGLE_1992); + board = new char[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + String letters = BOGGLE_1992[n*i+j]; + int r = StdRandom.uniform(letters.length()); + board[i][j] = letters.charAt(r); + } + } + } + + /** + * Initializes a board from the given filename. + * @param filename the name of the file containing the Boggle board + */ + public BoggleBoard(String filename) { + In in = new In(filename); + m = in.readInt(); + n = in.readInt(); + if (m <= 0) throw new IllegalArgumentException("number of rows must be a positive integer"); + if (n <= 0) throw new IllegalArgumentException("number of columns must be a positive integer"); + board = new char[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + String letter = in.readString().toUpperCase(); + if (letter.equals("QU")) + board[i][j] = 'Q'; + else if (letter.length() != 1) + throw new IllegalArgumentException("invalid character: " + letter); + else if (!ALPHABET.contains(letter)) + throw new IllegalArgumentException("invalid character: " + letter); + else + board[i][j] = letter.charAt(0); + } + } + } + + /** + * Initializes a random m-by-n board, according to the frequency + * of letters in the English language. + * @param m the number of rows + * @param n the number of columns + */ + public BoggleBoard(int m, int n) { + this.m = m; + this.n = n; + if (m <= 0) throw new IllegalArgumentException("number of rows must be a positive integer"); + if (n <= 0) throw new IllegalArgumentException("number of columns must be a positive integer"); + board = new char[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + int r = StdRandom.discrete(FREQUENCIES); + board[i][j] = ALPHABET.charAt(r); + } + } + } + + /** + * Initializes a board from the given 2d character array, + * with 'Q' representing the two-letter sequence "Qu". + * @param a the 2d character array + */ + public BoggleBoard(char[][] a) { + this.m = a.length; + if (m == 0) throw new IllegalArgumentException("number of rows must be a positive integer"); + this.n = a[0].length; + if (n == 0) throw new IllegalArgumentException("number of columns must be a positive integer"); + board = new char[m][n]; + for (int i = 0; i < m; i++) { + if (a[i].length != n) + throw new IllegalArgumentException("char[][] array is ragged"); + for (int j = 0; j < n; j++) { + if (ALPHABET.indexOf(a[i][j]) == -1) + throw new IllegalArgumentException("invalid character: " + a[i][j]); + board[i][j] = a[i][j]; + } + } + } + + /** + * Returns the number of rows. + * @return number of rows + */ + public int rows() { + return m; + } + + /** + * Returns the number of columns. + * @return number of columns + */ + public int cols() { + return n; + } + + /** + * Returns the letter in row i and column j, + * with 'Q' representing the two-letter sequence "Qu". + * @param i the row + * @param j the column + * @return the letter in row i and column j + * with 'Q' representing the two-letter sequence "Qu". + */ + public char getLetter(int i, int j) { + return board[i][j]; + } + + /** + * Returns a string representation of the board, replacing 'Q' with "Qu". + * @return a string representation of the board, replacing 'Q' with "Qu" + */ + public String toString() { + StringBuilder sb = new StringBuilder(m + " " + n + "\n"); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + sb.append(board[i][j]); + if (board[i][j] == 'Q') sb.append("u "); + else sb.append(" "); + } + sb.append("\n"); + } + return sb.toString().trim(); + } + + /** + * Unit tests the BoggleBoard data type. + */ + public static void main(String[] args) { + + // initialize a 4-by-4 board using Hasbro dice + StdOut.println("Hasbro board:"); + BoggleBoard board1 = new BoggleBoard(); + StdOut.println(board1); + StdOut.println(); + + // initialize a 4-by-4 board using letter frequencies in English language + StdOut.println("Random 4-by-4 board:"); + BoggleBoard board2 = new BoggleBoard(4, 4); + StdOut.println(board2); + StdOut.println(); + + // initialize a 4-by-4 board from a 2d char array + StdOut.println("4-by-4 board from 2D character array:"); + char[][] a = { + { 'D', 'O', 'T', 'Y' }, + { 'T', 'R', 'S', 'F' }, + { 'M', 'X', 'M', 'O' }, + { 'Z', 'A', 'B', 'W' } + }; + BoggleBoard board3 = new BoggleBoard(a); + StdOut.println(board3); + StdOut.println(); + + // initialize a 4-by-4 board from a file + String filename = "board-quinquevalencies.txt"; + StdOut.println("4-by-4 board from file " + filename + ":"); + BoggleBoard board4 = new BoggleBoard(filename); + StdOut.println(board4); + StdOut.println(); + } + +} diff --git a/Week4/src/BoggleSolver.java b/Week4/src/BoggleSolver.java new file mode 100644 index 0000000..5fde7d8 --- /dev/null +++ b/Week4/src/BoggleSolver.java @@ -0,0 +1,315 @@ +import edu.princeton.cs.algs4.TST; +import edu.princeton.cs.algs4.In; +import edu.princeton.cs.algs4.StdOut; + +public class BoggleSolver +{ + private BoggleDict dict; + private static final int FIRST_LETTER = 65; + private static final int[] SCORE = new int[]{1,1,2,3,5,11}; + + // Initializes the data structure using the given array of strings as the dictionary. + // (You can assume each word in the dictionary contains only the uppercase letters A through Z.) + public BoggleSolver(String[] dictionary){ + dict = new BoggleDict(dictionary); + } + + // Returns the set of all valid words in the given Boggle board, as an Iterable. + public Iterable getAllValidWords(BoggleBoard board){ + int rows = board.rows(); + int cols = board.cols(); + // Because the board is a dense graph + // Just rely on row and col counts + + // Prepare an array to indicate that a letter is used + boolean[][] used = new boolean[rows][cols]; + // Keep TST for found words + TST found_words = new TST(); + + // This section could be cleaner + for (int r=0;r found_words, int r, int c){ + // perform DFS starting from the third letter + // During DFS recursion, check if the letter can be traced from the current node + char letter = board.getLetter(r, c); + TSTNode node = tst.get_next(tst_node, letter); + int n_chars = sBuilder.length() + 1; + + // Find an extra U if the letter is Q + if (letter == 'Q'){ + node = tst.step_into(node); + node = tst.get_next(node, 'U'); + n_chars++; + } + + if (node != null){ + // If so, mark the current char and build the string + StringBuilder next_sBuilder = new StringBuilder(n_chars); + next_sBuilder.append(sBuilder.toString()); + used[r][c] = true; + next_sBuilder.append(letter); + + // Add the U if the letter is Q + if (letter == 'Q') + next_sBuilder.append('U'); + + // Get the string if it is a valid word (i.e. score is non-zero) + // Also need to check if word already added + if (node.val >0){ + found_words.put(next_sBuilder.toString(), node.val); + } + + // Recurse the search on adjacents + for(int i=-1;i<2;i++){ + int next_r = r+i; + if(next_r == -1 || next_r == board.rows()) + continue; + for(int j=-1;j<2;j++){ + int next_c = c+j; + if(next_c == -1 || next_c == board.cols()) + continue; + + if (!used[next_r][next_c]){ + recursive_search(board, tst, tst.step_into(node), used, next_sBuilder, found_words, next_r, next_c); + } + + } + } + + // When finish with a node, unmark the current letter + used[r][c] = false; + } + } + + // Returns the score of the given word if it is in the dictionary, zero otherwise. + // (You can assume the word contains only the uppercase letters A through Z.) + public int scoreOf(String word){ + if(word.length()<3) + return 0; + ManualTST tst = dict.get_TST(word); + if(tst != null){ + return tst.get(word.substring(2)); + } + return 0; + } + + // The dictionary is a TST+R^2 implementation + // This is used since Boggle expects 3 letters or more + // It is also expected that the chars are Uppercase + // First letter A - 65 in decimal + private class BoggleDict{ + private ManualTST[][] roots; // root of TST, use an array for R2 + + public BoggleDict(String[] dictionary){ + roots = new ManualTST[26][26]; + for(String word : dictionary){ + int word_len = word.length(); + if (word_len>=3){ + //Process here + int char1 = word.charAt(0) - FIRST_LETTER; + int char2 = word.charAt(1) - FIRST_LETTER; + String remaining = word.substring(2); + int score_ind = Math.min(word_len-2, SCORE.length)-1; + if (roots[char1][char2] == null){ + roots[char1][char2] = new ManualTST(); + } + roots[char1][char2].put(remaining, SCORE[score_ind]); + } + } + } + + public ManualTST get_TST(String word){ + if (word==null) return null; + int char1 = word.charAt(0) - FIRST_LETTER; + int char2 = word.charAt(1) - FIRST_LETTER; + return roots[char1][char2]; + } + public ManualTST get_TST(char c1, char c2){ + int char1 = c1 - FIRST_LETTER; + int char2 = c2 - FIRST_LETTER; + return roots[char1][char2]; + } + } + + private class TSTNode { + private char c; // character + private TSTNode left, mid, right; // left, middle, and right subtries + public int val; // Score for the string + } + + // This TST is a striped down version of the one in algs4 + // Consisting get and put + // with two extra functions for manual navigation + private class ManualTST{ + public TSTNode root; // root of TST + + public int get(String key) { + if (key == null) { + throw new IllegalArgumentException("calls get() with null argument"); + } + if (key.length() == 0) throw new IllegalArgumentException("key must have length >= 1"); + TSTNode x = get(root, key, 0); + if (x == null) return 0; + return x.val; + } + + // return subtrie corresponding to given key + private TSTNode get(TSTNode x, String key, int d) { + if (x == null) return null; + if (key.length() == 0) throw new IllegalArgumentException("key must have length >= 1"); + char c = key.charAt(d); + if (c < x.c) return get(x.left, key, d); + else if (c > x.c) return get(x.right, key, d); + else if (d < key.length() - 1) return get(x.mid, key, d+1); + else return x; + } + + public void put(String key, int val) { + if (key == null) { + throw new IllegalArgumentException("calls put() with null key"); + } + root = put(root, key, val, 0); + } + + private TSTNode put(TSTNode x, String key, int val, int d) { + char c = key.charAt(d); + if (x == null) { + x = new TSTNode(); + x.c = c; + } + if (c < x.c) x.left = put(x.left, key, val, d); + else if (c > x.c) x.right = put(x.right, key, val, d); + else if (d < key.length() - 1) x.mid = put(x.mid, key, val, d+1); + else x.val = val; + return x; + } + + // Recursively find the node with the next char + // Stopping before entering the mid + public TSTNode get_next(TSTNode x, char next_char){ + if (x == null) return null; + if (next_char < x.c) return get_next(x.left, next_char); + else if (next_char > x.c) return get_next(x.right, next_char); + else return x; + } + // Enter the mid of the node + // Should be called after get_next if it gets a node + public TSTNode step_into(TSTNode x){ + if (x == null) return null; + return x.mid; + } + } + + public static void main(String[] args){ + In in = new In("./boards/dictionary-enable2k.txt"); + String[] dictionary = in.readAllStrings(); + + BoggleSolver solver = new BoggleSolver(dictionary); + System.out.println("done"); + String[] words = new String[]{"TIE", "SYNCHRONIZATION","HAHA", "BUY","BUSY","AO"}; + for(String word : words){ + System.out.println(solver.scoreOf(word)); + } + + int trials = 1; + + BoggleBoard board ; + for(int i=0;i0){ + rank = (new Quick3Suffix()).sort(s); + } + + } + + // length of s + public int length(){ + return sLen; + } + + // returns index of ith sorted suffix + public int index(int i){ + if(i<0 || i>= sLen) + throw new IllegalArgumentException("Index out of range"); + + return rank[i]; + } + + // Sort on the possible circular suffixes + // There is no String on the suffixes + // The shift in the original string represented with an int + // During sorting, the shifts representing the suffixes + // are moved around + // This is a variant of the Quick3String for n*Log(n) performance + // LSD is too slow for long string + private class Quick3Suffix { + + private static final int CUTOFF = 15; // cutoff to insertion sort + + public int[] sort(String a) { + int[] shifts = new int[sLen]; + for(int i=0;i= 0 && d <= s.length(); + if (d == s.length()) return -1; + return s.charAt((d+shift)%sLen); + } + + // 3-way string quicksort a[lo..hi] starting at dth character + private void sort(String a, int lo, int hi, int d, int[] shifts) { + + // cutoff to insertion sort for small subarrays + if (hi <= lo + CUTOFF) { + insertion(a, lo, hi, d, shifts); + return; + } + + int lt = lo, gt = hi; + int v = charAt(a, d, shifts[lo]); + int i = lo + 1; + while (i <= gt) { + int t = charAt(a, d, shifts[i]); + if (t < v) exch(shifts, lt++, i++); + else if (t > v) exch(shifts, i, gt--); + else i++; + } + + // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. + sort(a, lo, lt-1, d, shifts); + if (v >= 0) sort(a, lt, gt, d+1, shifts); + sort(a, gt+1, hi, d, shifts); + } + + private void insertion(String a, int lo, int hi, int d, int[] shifts) { + for (int i = lo; i <= hi; i++) + for (int j = i; j > lo && less(a, shifts[j], shifts[j-1], d); j--) + exch(shifts, j, j-1); + } + + private void exch(int[] shifts, int i, int j) { + int temp = shifts[i]; + shifts[i] = shifts[j]; + shifts[j] = temp; + } + + private boolean less(String a, int shift_v, int shift_w, int d) { + for (int i = d; i < sLen; i++) { + if (charAt(a, i, shift_v) < charAt(a, i, shift_w)) return true; + if (charAt(a, i, shift_v) > charAt(a, i, shift_w)) return false; + } + return false; + } + } + + // unit testing (required) + public static void main(String[] args){ + CircularSuffixArray sArray = new CircularSuffixArray("ABRACADABRA!"); + + int sLen = sArray.length(); + for(int i=0;i=0;i--){ + char c1 = characters[i]; + rank[c1]++; + characters[i+1] = c1; + } + // Place the char in the front + characters[0] = c; + rank[c] = 0; + } + BinaryStdOut.close(); + } + + // apply move-to-front decoding, reading from standard input and writing to standard output + public static void decode(){ + char[] characters = new char[EXTENDED_ASCII]; + int[] rank = new int[EXTENDED_ASCII]; + for(int i=0;i=0;i--){ + char c1 = characters[i]; + rank[c1]++; + characters[i+1] = c1; + } + // Place the char in the front + characters[0] = c; + rank[c] = 0; + } + BinaryStdOut.close(); + + } + + // if args[0] is "-", apply move-to-front encoding + // if args[0] is "+", apply move-to-front decoding + public static void main(String[] args){ + if (args[0].equals("-")) + encode(); + else if (args[0].equals("+")) + decode(); + } + +}