diff --git a/src/main/java/ru/spbau/mit/StringSetImpl.java b/src/main/java/ru/spbau/mit/StringSetImpl.java new file mode 100644 index 0000000..caf1b83 --- /dev/null +++ b/src/main/java/ru/spbau/mit/StringSetImpl.java @@ -0,0 +1,144 @@ +package ru.spbau.mit; + +public class StringSetImpl implements StringSet { + + private int size; + private Node root; + + StringSetImpl() { + root = new Node('a'); + } + + @Override + public boolean add(String element) { + + if (contains(element)) { + return false; + } + + Node node = root; + for (char value : element.toCharArray()) { + Node child = node.getChild(value); + if (child == null) { + child = new Node(value); + child.parent = node; + node.addChild(child); + + } + node.howManyStartsWithPrefix++; + node = child; + } + + node.isFullWord = true; + node.howManyStartsWithPrefix++; + size++; + + return true; + + } + + private Node findLastNode(String element) { + + Node node = root; + for (char value : element.toCharArray()) { + Node child = node.getChild(value); + if (child == null) { + return null; + } + node = child; + } + + return node; + + } + + @Override + public boolean contains(String element) { + + Node node = findLastNode(element); + + return node != null && node.isFullWord; + } + + private int getIndexByChar(char value) { + final int asciiOffset = 65; + return value - asciiOffset; + } + + @Override + public boolean remove(String element) { + + if (!contains(element)) { + return false; + } + + Node lastNode = findLastNode(element); + lastNode.isFullWord = false; + lastNode.howManyStartsWithPrefix--; + size--; + + char[] array = element.toCharArray(); + Node parent = lastNode.parent; + + // decrement value on path to root + for (int i = element.length() - 1; i >= 0; i--) { + parent.howManyStartsWithPrefix--; + parent = parent.parent; + } + + // delete unused prefix + if (lastNode.numberOfChildren == 0) { + parent = lastNode.parent; + for (int i = element.length() - 1; i >= 0; i--) { + parent.numberOfChildren--; + parent.children[getIndexByChar(array[i])] = null; + if (parent.isFullWord || parent.numberOfChildren > 0) { + break; + } + parent = parent.parent; + } + } + + return true; + } + + @Override + public int size() { + return size; + } + + @Override + public int howManyStartsWithPrefix(String prefix) { + Node lastNode = findLastNode(prefix); + if (lastNode == null) { + return 0; + } + return lastNode.howManyStartsWithPrefix; + } + + class Node { + + private Node[] children; + private char value; + private int numberOfChildren; + private int howManyStartsWithPrefix; + private boolean isFullWord; + private Node parent; + + + Node(char value) { + final int sizeOfFrameInAscii = 58; + this.value = value; + this.children = new Node[sizeOfFrameInAscii]; + } + + Node getChild(char value) { + return children[getIndexByChar(value)]; + } + + void addChild(final Node newChild) { + children[getIndexByChar(newChild.value)] = newChild; + numberOfChildren++; + } + } +} diff --git a/src/test/java/ru/spbau/mit/StringSetTest.java b/src/test/java/ru/spbau/mit/StringSetTest.java index 4eee756..f558ab4 100644 --- a/src/test/java/ru/spbau/mit/StringSetTest.java +++ b/src/test/java/ru/spbau/mit/StringSetTest.java @@ -15,11 +15,141 @@ public void testSimple() { assertTrue(stringSet.contains("abc")); assertEquals(1, stringSet.size()); assertEquals(1, stringSet.howManyStartsWithPrefix("abc")); + assertTrue(stringSet.remove("abc")); + assertTrue(!stringSet.contains("abc")); } + @Test + public void moreGeneralTest() { + StringSet stringSet = instance(); + + assertTrue(stringSet.add("A")); + assertTrue(stringSet.add("in")); + assertTrue(stringSet.add("inn")); + assertTrue(stringSet.add("to")); + assertTrue(stringSet.add("tea")); + assertTrue(stringSet.add("ted")); + assertTrue(stringSet.add("ten")); + + assertTrue(stringSet.contains("A")); + assertTrue(stringSet.contains("to")); + assertTrue(stringSet.contains("tea")); + assertTrue(stringSet.contains("ted")); + assertTrue(stringSet.contains("ten")); + assertTrue(stringSet.contains("inn")); + assertTrue(stringSet.contains("in")); + + final int size = 7; + assertTrue(stringSet.size() == size); + + assertTrue(stringSet.howManyStartsWithPrefix("i") == 2); + assertTrue(stringSet.howManyStartsWithPrefix("in") == 2); + assertTrue(stringSet.howManyStartsWithPrefix("te") + == size - (size / 2) - 1); // :) + + assertTrue(stringSet.remove("in")); + + assertTrue(stringSet.size() == size - 1); + assertTrue(!stringSet.contains("in")); + assertTrue(stringSet.contains("inn")); + assertTrue(stringSet.howManyStartsWithPrefix("in") == 1); + assertTrue(stringSet.howManyStartsWithPrefix("te") + == size - (size / 2) - 1); + assertTrue(stringSet.howManyStartsWithPrefix("tea") == 1); + assertTrue(stringSet.howManyStartsWithPrefix("ted") == 1); + assertTrue(stringSet.howManyStartsWithPrefix("ten") == 1); + + assertTrue(!stringSet.remove("te")); + assertTrue(stringSet.size() == size - 1); + + assertTrue(stringSet.howManyStartsWithPrefix("te") + == size - (size / 2) - 1); + assertTrue(stringSet.howManyStartsWithPrefix("tea") == 1); + assertTrue(stringSet.howManyStartsWithPrefix("ted") == 1); + assertTrue(stringSet.howManyStartsWithPrefix("ten") == 1); + + assertTrue(stringSet.remove("tea")); + assertTrue(stringSet.size() == size - 2); + + assertTrue(stringSet.howManyStartsWithPrefix("te") == 2); + assertTrue(stringSet.howManyStartsWithPrefix("tea") == 0); + assertTrue(stringSet.howManyStartsWithPrefix("ted") == 1); + assertTrue(stringSet.howManyStartsWithPrefix("ten") == 1); + + } + + @Test + public void addContainsSizeRemoveSizeContains() { + StringSet stringSet = instance(); + + stringSet.add("abc"); + stringSet.add("abd"); + stringSet.add("bcd"); + + assertTrue(stringSet.contains("abc")); + assertTrue(stringSet.size() == 1 + 1 + 1); + assertTrue(stringSet.remove("abc")); + assertTrue(stringSet.size() == 2); + assertTrue(!stringSet.contains("abc")); + } + + @Test + public void addContainsPrefixRemovePrefix() { + StringSet stringSet = instance(); + + stringSet.add("abc"); + stringSet.add("abd"); + stringSet.add("bcd"); + + assertTrue(stringSet.contains("abc")); + assertTrue(stringSet.howManyStartsWithPrefix("a") == 2); + assertTrue(stringSet.remove("abc")); + assertTrue(stringSet.howManyStartsWithPrefix("a") == 1); + } + + @Test + public void doubleAdd() { + StringSet stringSet = instance(); + stringSet.add("helloWorldAndVeryLongStringAfter"); + stringSet.add("helloWorldAndVeryLongStringAfter"); + assertTrue(stringSet.size() == 1); + assertTrue(stringSet.howManyStartsWithPrefix("helloWorld") == 1); + } + + @Test + public void doubleDelete() { + StringSet stringSet = instance(); + stringSet.add("helloWorldAndVeryLongStringAfter"); + + assertTrue(stringSet.remove("helloWorldAndVeryLongStringAfter")); + assertTrue(!stringSet.remove("helloWorldAndVeryLongStringAfter")); + + assertTrue(stringSet.size() == 0); + assertTrue(stringSet.howManyStartsWithPrefix("helloWorld") == 0); + } + + @Test + public void emptyStringsTest() { + StringSet stringSet = instance(); + + stringSet.add("abc"); + assertTrue(stringSet.howManyStartsWithPrefix("") == 1); + assertTrue(stringSet.add("")); + assertTrue(stringSet.howManyStartsWithPrefix("") == 2); + assertTrue(!stringSet.add("")); + assertTrue(stringSet.howManyStartsWithPrefix("") == 2); + assertTrue(stringSet.size() == 2); + assertTrue(stringSet.remove("")); + assertTrue(stringSet.size() == 1); + assertTrue(stringSet.contains("abc")); + } + + + public static StringSet instance() { try { - return (StringSet) Class.forName("ru.spbau.mit.StringSetImpl").newInstance(); + return (StringSet) Class.forName("ru.spbau.mit.StringSetImpl") + .newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) {