diff --git a/Trie/.gitignore b/Trie/.gitignore new file mode 100644 index 0000000..345e61a --- /dev/null +++ b/Trie/.gitignore @@ -0,0 +1,49 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/Trie/.idea/compiler.xml b/Trie/.idea/compiler.xml new file mode 100644 index 0000000..3b1aaf8 --- /dev/null +++ b/Trie/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Trie/.idea/misc.xml b/Trie/.idea/misc.xml new file mode 100644 index 0000000..56c64d0 --- /dev/null +++ b/Trie/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/Trie/.idea/modules.xml b/Trie/.idea/modules.xml new file mode 100644 index 0000000..33b02ce --- /dev/null +++ b/Trie/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Trie/Trie.iml b/Trie/Trie.iml new file mode 100644 index 0000000..00b58f0 --- /dev/null +++ b/Trie/Trie.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Trie/pom.xml b/Trie/pom.xml new file mode 100644 index 0000000..7f0f184 --- /dev/null +++ b/Trie/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + ru.spbau.mit.kazakov + Trie + 1.0-SNAPSHOT + jar + + + + junit + junit + 4.0 + + + + \ No newline at end of file diff --git a/Trie/src/main/java/ru/spbau/mit/kazakov/Trie/Trie.java b/Trie/src/main/java/ru/spbau/mit/kazakov/Trie/Trie.java new file mode 100644 index 0000000..4caf359 --- /dev/null +++ b/Trie/src/main/java/ru/spbau/mit/kazakov/Trie/Trie.java @@ -0,0 +1,133 @@ +package ru.spbau.mit.kazakov.Trie; + +import java.io.*; +import java.util.Hashtable; + +/** + * Prefix tree for storing strings. + */ +public class Trie implements Serializable { + private Node root = new Node(); + + /** + * Checks if there is specified string in trie. + * + * @param element a string to check + * @return true if there is specified string in trie, and false otherwise + */ + public boolean contains(String element) { + Node current = root; + + for (int i = 0; i < element.length(); i++) { + if (!current.edges.containsKey(element.charAt(i))) { + return false; + } + current = current.edges.get(element.charAt(i)); + } + + return current.isTerminal; + } + + /** + * Adds specified string to trie. + * + * @param element a string to add + * @return true if there was no specified string in trie, and false otherwise + */ + public boolean add(String element) { + if (contains(element)) { + return false; + } + + Node current = root; + for (int i = 0; i < element.length(); i++) { + current.numOfContinuations++; + if (!current.edges.containsKey(element.charAt(i))) { + current.edges.put(element.charAt(i), new Node()); + } + current = current.edges.get(element.charAt(i)); + } + current.numOfContinuations++; + current.isTerminal = true; + + return true; + } + + /** + * Counts how many strings in trie starts with specified string. + */ + public int howManyStartsWithPrefix(String prefix) { + Node current = root; + + for (int i = 0; i < prefix.length(); i++) { + if (!current.edges.containsKey(prefix.charAt(i))) { + return 0; + } + current = current.edges.get(prefix.charAt(i)); + } + + return current.numOfContinuations; + } + + /** + * Returns number of added strings in trie. + */ + public int size() { + return root.numOfContinuations; + } + + /** + * Removes specified string from trie. + * + * @param element a string to remove + * @return true if there was specified string in trie, and false otherwise + */ + public boolean remove(String element) { + if (!contains(element)) { + return false; + } + + Node current = root; + for (int i = 0; i < element.length(); i++) { + current.numOfContinuations--; + Node next = current.edges.get(element.charAt(i)); + if (next.numOfContinuations == 1) { + current.edges.remove(element.charAt(i)); + } + current = next; + } + current.numOfContinuations--; + current.isTerminal = false; + + return true; + } + + /** + * Serializes current trie to given stream. + * + * @param out stream for writing + */ + public void serialize(OutputStream out) throws IOException { + ObjectOutputStream objectOutputStream = new ObjectOutputStream(out); + objectOutputStream.writeObject(this); + } + + /** + * Deserializes trie from given stream and replaces current trie with new one. + * + * @param in stream for reading + */ + public void deserialize(InputStream in) throws IOException, ClassNotFoundException { + ObjectInputStream objectInputStream = new ObjectInputStream(new BufferedInputStream(in)); + root = ((Trie) objectInputStream.readObject()).root; + } + + /** + * Vertex in trie. Stores outgoing edges with corresponding characters. + */ + private class Node implements Serializable { + private Hashtable edges = new Hashtable(); + private boolean isTerminal = false; + private int numOfContinuations = 0; + } +} diff --git a/Trie/src/test/java/ru/spbau/mit/kazakov/Trie/TrieTest.java b/Trie/src/test/java/ru/spbau/mit/kazakov/Trie/TrieTest.java new file mode 100644 index 0000000..973f392 --- /dev/null +++ b/Trie/src/test/java/ru/spbau/mit/kazakov/Trie/TrieTest.java @@ -0,0 +1,173 @@ +package ru.spbau.mit.kazakov.Trie; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static org.junit.Assert.*; + +public class TrieTest { + @Test + public void testConstructor() { + Trie trie = new Trie(); + } + + @Test + public void testAddReturnTrue() { + Trie trie = new Trie(); + assertTrue(trie.add("some string")); + } + + @Test + public void testAddReturnFalse() { + Trie trie = new Trie(); + trie.add("some string"); + assertFalse(trie.add("some string")); + } + + @Test + public void testRemoveEmptyTrie() { + Trie trie = new Trie(); + assertFalse(trie.remove("some string")); + } + + @Test + public void testRemoveNotExisting() { + Trie trie = new Trie(); + trie.add("some string"); + assertFalse(trie.remove("another string")); + } + + @Test + public void testRemoveExisting() { + Trie trie = new Trie(); + trie.add("some string"); + assertTrue(trie.remove("some string")); + } + + @Test + public void testContainsEmptyTrie() { + Trie trie = new Trie(); + assertFalse(trie.contains("some string")); + } + + @Test + public void testContainsExisting() { + Trie trie = new Trie(); + trie.add("some string"); + assertTrue(trie.contains("some string")); + } + + @Test + public void testContainsNotExisting() { + Trie trie = new Trie(); + trie.add("some string"); + assertFalse(trie.contains("another string")); + } + + @Test + public void testContainsRemoved() { + Trie trie = new Trie(); + trie.add("some string"); + trie.remove("some string"); + assertFalse(trie.contains("some string")); + } + + @Test + public void testHowManyStartsWithPrefixEmptyTrie() { + Trie trie = new Trie(); + assertEquals(0, trie.howManyStartsWithPrefix("")); + } + + @Test + public void testHowManyStartsWithPrefixEmptyString() { + Trie trie = new Trie(); + trie.add("some string"); + trie.add("another string"); + assertEquals(2, trie.howManyStartsWithPrefix("")); + } + + @Test + public void testHowManyStartsWithPrefixAddedString() { + Trie trie = new Trie(); + trie.add("some string"); + trie.add("another string"); + assertEquals(1, trie.howManyStartsWithPrefix("some string")); + } + + @Test + public void testHowManyStartsWithPrefixTwoStrings() { + Trie trie = new Trie(); + trie.add("super string"); + trie.add("super mega string"); + assertEquals(2, trie.howManyStartsWithPrefix("super ")); + } + + @Test + public void testHowManyStartsWithPrefixRemovedString() { + Trie trie = new Trie(); + trie.add("some string"); + trie.remove("some string"); + assertEquals(0, trie.howManyStartsWithPrefix("some")); + } + + @Test + public void testSizeEmptyTrie() { + Trie trie = new Trie(); + assertEquals(0, trie.size()); + } + + @Test + public void testSizeThree() { + Trie trie = new Trie(); + trie.add("first string"); + trie.add("second string"); + trie.add("third string"); + assertEquals(3, trie.size()); + } + + @Test + public void testSizeRemove() { + Trie trie = new Trie(); + trie.add("first string"); + trie.add("second string"); + trie.add("third string"); + trie.remove("first string"); + assertEquals(2, trie.size()); + } + + @Test + public void testSerializeDeserializeEmpty() throws IOException, ClassNotFoundException { + Trie trie = new Trie(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + trie.serialize(os); + byte[] serialized = os.toByteArray(); + + ByteArrayInputStream is = new ByteArrayInputStream(serialized); + Trie newTrie = new Trie(); + newTrie.add("some string"); + newTrie.deserialize(is); + + assertFalse(newTrie.contains("some string")); + assertEquals(0, newTrie.size()); + } + + @Test + public void testSerializeDeserializeNotEmpty() throws IOException, ClassNotFoundException { + Trie trie = new Trie(); + trie.add("some string"); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + trie.serialize(os); + byte[] serialized = os.toByteArray(); + + Trie newTrie = new Trie(); + newTrie.add("another string"); + ByteArrayInputStream is = new ByteArrayInputStream(serialized); + newTrie.deserialize(is); + + assertTrue(newTrie.contains("some string")); + assertFalse(newTrie.contains("another string")); + } +} \ No newline at end of file