Issue #1 remove overlapping intervals. Resolution rule: longer matches over smaller ones, left-most over right-most
This commit is contained in:
parent
922da26965
commit
1785a554f3
19
README.md
19
README.md
@ -1,6 +1,17 @@
|
|||||||
Aho-Corasick
|
Aho-Corasick
|
||||||
============
|
============
|
||||||
|
|
||||||
|
Dependency
|
||||||
|
----------
|
||||||
|
Include this dependency in your POM. Be sure to check for the latest version in Maven Central.
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ahocorasick</groupId>
|
||||||
|
<artifactId>ahocorasick</artifactId>
|
||||||
|
<version>0.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
Nowadays most free-text searching is based on Lucene-like approaches, where the search text is parsed into its
|
Nowadays most free-text searching is based on Lucene-like approaches, where the search text is parsed into its
|
||||||
@ -49,11 +60,9 @@ Setting up the Trie is a piece of cake:
|
|||||||
```
|
```
|
||||||
|
|
||||||
You can now read the set. In this case it will find the following:
|
You can now read the set. In this case it will find the following:
|
||||||
* "she" at position 3
|
* "she" starting at position 1, ending at position 3
|
||||||
* "he" at position 3
|
* "he" starting at position 2, ending at position 3
|
||||||
* "hers" at position 5
|
* "hers" starting at position 2, ending at position 5
|
||||||
|
|
||||||
Note that the end-positions of the keyword match are emitted, not the start-positions!
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|||||||
4
pom.xml
4
pom.xml
@ -1,8 +1,8 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>org.aho-corasick</groupId>
|
<groupId>org.ahocorasick</groupId>
|
||||||
<artifactId>efficient-string-matching</artifactId>
|
<artifactId>ahocorasick</artifactId>
|
||||||
<version>0.2.0-SNAPSHOT</version>
|
<version>0.2.0-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>Aho-CoraSick algorithm for efficient string matching</name>
|
<name>Aho-CoraSick algorithm for efficient string matching</name>
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
package org.ahocorasick;
|
|
||||||
|
|
||||||
public class Emit {
|
|
||||||
private final int start;
|
|
||||||
private final int end;
|
|
||||||
private final String keyword;
|
|
||||||
|
|
||||||
public Emit(final int start, final int end, final String keyword) {
|
|
||||||
this.start = start;
|
|
||||||
this.end = end;
|
|
||||||
this.keyword = keyword;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getStart() {
|
|
||||||
return this.start;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getEnd() {
|
|
||||||
return this.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKeyword() {
|
|
||||||
return this.keyword;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
57
src/main/java/org/ahocorasick/interval/Interval.java
Normal file
57
src/main/java/org/ahocorasick/interval/Interval.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
public class Interval implements Intervalable {
|
||||||
|
|
||||||
|
private int start;
|
||||||
|
private int end;
|
||||||
|
|
||||||
|
public Interval(final int start, final int end) {
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStart() {
|
||||||
|
return this.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEnd() {
|
||||||
|
return this.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return end - start + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean overlapsWith(Interval other) {
|
||||||
|
return this.start <= other.getEnd() &&
|
||||||
|
this.end >= other.getStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean overlapsWith(int point) {
|
||||||
|
return this.start <= point && point <= this.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof Intervalable)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Intervalable other = (Intervalable)o;
|
||||||
|
return this.start == other.getStart() &&
|
||||||
|
this.end == other.getEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.start % 100 + this.end % 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Object o) {
|
||||||
|
if (!(o instanceof Intervalable)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Intervalable other = (Intervalable)o;
|
||||||
|
return this.start - other.getStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
119
src/main/java/org/ahocorasick/interval/IntervalNode.java
Normal file
119
src/main/java/org/ahocorasick/interval/IntervalNode.java
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class IntervalNode {
|
||||||
|
|
||||||
|
private enum Direction { LEFT, RIGHT }
|
||||||
|
|
||||||
|
private IntervalNode left = null;
|
||||||
|
private IntervalNode right = null;
|
||||||
|
private int point;
|
||||||
|
private List<Intervalable> intervals = new ArrayList<Intervalable>();
|
||||||
|
|
||||||
|
public IntervalNode(List<Intervalable> intervals) {
|
||||||
|
this.point = determineMedian(intervals);
|
||||||
|
|
||||||
|
List<Intervalable> toLeft = new ArrayList<Intervalable>();
|
||||||
|
List<Intervalable> toRight = new ArrayList<Intervalable>();
|
||||||
|
|
||||||
|
for (Intervalable interval : intervals) {
|
||||||
|
if (interval.getEnd() < this.point) {
|
||||||
|
toLeft.add(interval);
|
||||||
|
} else if (interval.getStart() > this.point) {
|
||||||
|
toRight.add(interval);
|
||||||
|
} else {
|
||||||
|
this.intervals.add(interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toLeft.size() > 0) {
|
||||||
|
this.left = new IntervalNode(toLeft);
|
||||||
|
}
|
||||||
|
if (toRight.size() > 0) {
|
||||||
|
this.right = new IntervalNode(toRight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int determineMedian(List<Intervalable> intervals) {
|
||||||
|
int start = -1;
|
||||||
|
int end = -1;
|
||||||
|
for (Intervalable interval : intervals) {
|
||||||
|
int currentStart = interval.getStart();
|
||||||
|
int currentEnd = interval.getEnd();
|
||||||
|
if (start == -1 || currentStart < start) {
|
||||||
|
start = currentStart;
|
||||||
|
}
|
||||||
|
if (end == -1 || currentEnd > end) {
|
||||||
|
end = currentEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (start + end) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Intervalable> findOverlaps(Intervalable interval) {
|
||||||
|
|
||||||
|
List<Intervalable> overlaps = new ArrayList<Intervalable>();
|
||||||
|
|
||||||
|
if (this.point < interval.getStart()) { // Tends to the right
|
||||||
|
addToOverlaps(interval, overlaps, findOverlappingRanges(this.right, interval));
|
||||||
|
addToOverlaps(interval, overlaps, checkForOverlapsToTheRight(interval));
|
||||||
|
} else if (this.point > interval.getEnd()) { // Tends to the left
|
||||||
|
addToOverlaps(interval, overlaps, findOverlappingRanges(this.left, interval));
|
||||||
|
addToOverlaps(interval, overlaps, checkForOverlapsToTheLeft(interval));
|
||||||
|
} else { // Somewhere in the middle
|
||||||
|
addToOverlaps(interval, overlaps, this.intervals);
|
||||||
|
addToOverlaps(interval, overlaps, findOverlappingRanges(this.left, interval));
|
||||||
|
addToOverlaps(interval, overlaps, findOverlappingRanges(this.right, interval));
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addToOverlaps(Intervalable interval, List<Intervalable> overlaps, List<Intervalable> newOverlaps) {
|
||||||
|
for (Intervalable currentInterval : newOverlaps) {
|
||||||
|
if (!currentInterval.equals(interval)) {
|
||||||
|
overlaps.add(currentInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Intervalable> checkForOverlapsToTheLeft(Intervalable interval) {
|
||||||
|
return checkForOverlaps(interval, Direction.LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Intervalable> checkForOverlapsToTheRight(Intervalable interval) {
|
||||||
|
return checkForOverlaps(interval, Direction.RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Intervalable> checkForOverlaps(Intervalable interval, Direction direction) {
|
||||||
|
|
||||||
|
List<Intervalable> overlaps = new ArrayList<Intervalable>();
|
||||||
|
for (Intervalable currentInterval : this.intervals) {
|
||||||
|
switch (direction) {
|
||||||
|
case LEFT :
|
||||||
|
if (currentInterval.getStart() <= interval.getEnd()) {
|
||||||
|
overlaps.add(currentInterval);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RIGHT :
|
||||||
|
if (currentInterval.getEnd() >= interval.getStart()) {
|
||||||
|
overlaps.add(currentInterval);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return overlaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected List<Intervalable> findOverlappingRanges(IntervalNode node, Intervalable interval) {
|
||||||
|
if (node != null) {
|
||||||
|
return node.findOverlaps(interval);
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
48
src/main/java/org/ahocorasick/interval/IntervalTree.java
Normal file
48
src/main/java/org/ahocorasick/interval/IntervalTree.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
public class IntervalTree {
|
||||||
|
|
||||||
|
private IntervalNode rootNode = null;
|
||||||
|
|
||||||
|
public IntervalTree(List<Intervalable> intervals) {
|
||||||
|
this.rootNode = new IntervalNode(intervals);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Intervalable> removeOverlaps(List<Intervalable> intervals) {
|
||||||
|
|
||||||
|
// Sort the intervals on size, then left-most position
|
||||||
|
Collections.sort(intervals, new IntervalableComparatorBySize());
|
||||||
|
|
||||||
|
Set<Intervalable> removeIntervals = new TreeSet<Intervalable>();
|
||||||
|
|
||||||
|
for (Intervalable interval : intervals) {
|
||||||
|
// If the interval was already removed, ignore it
|
||||||
|
if (removeIntervals.contains(interval)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all overallping intervals
|
||||||
|
removeIntervals.addAll(findOverlaps(interval));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all intervals that were overlapping
|
||||||
|
for (Intervalable removeInterval : removeIntervals) {
|
||||||
|
intervals.remove(removeInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the intervals, now on left-most position only
|
||||||
|
Collections.sort(intervals, new IntervalableComparatorByPosition());
|
||||||
|
|
||||||
|
return intervals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Intervalable> findOverlaps(Intervalable interval) {
|
||||||
|
return rootNode.findOverlaps(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
9
src/main/java/org/ahocorasick/interval/Intervalable.java
Normal file
9
src/main/java/org/ahocorasick/interval/Intervalable.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
public interface Intervalable extends Comparable {
|
||||||
|
|
||||||
|
public int getStart();
|
||||||
|
public int getEnd();
|
||||||
|
public int size();
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
public class IntervalableComparatorByPosition implements Comparator<Intervalable> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Intervalable intervalable, Intervalable intervalable2) {
|
||||||
|
return intervalable.getStart() - intervalable2.getStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
public class IntervalableComparatorBySize implements Comparator<Intervalable> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Intervalable intervalable, Intervalable intervalable2) {
|
||||||
|
int comparison = intervalable2.size() - intervalable.size();
|
||||||
|
if (comparison == 0) {
|
||||||
|
comparison = intervalable.getStart() - intervalable2.getStart();
|
||||||
|
}
|
||||||
|
return comparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
src/main/java/org/ahocorasick/trie/Emit.java
Normal file
19
src/main/java/org/ahocorasick/trie/Emit.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package org.ahocorasick.trie;
|
||||||
|
|
||||||
|
import org.ahocorasick.interval.Interval;
|
||||||
|
import org.ahocorasick.interval.Intervalable;
|
||||||
|
|
||||||
|
public class Emit extends Interval implements Intervalable {
|
||||||
|
|
||||||
|
private final String keyword;
|
||||||
|
|
||||||
|
public Emit(final int start, final int end, final String keyword) {
|
||||||
|
super(start, end);
|
||||||
|
this.keyword = keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyword() {
|
||||||
|
return this.keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package org.ahocorasick;
|
package org.ahocorasick.trie;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -1,4 +1,7 @@
|
|||||||
package org.ahocorasick;
|
package org.ahocorasick.trie;
|
||||||
|
|
||||||
|
import org.ahocorasick.interval.IntervalTree;
|
||||||
|
import org.ahocorasick.interval.Intervalable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -13,14 +16,26 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
*/
|
*/
|
||||||
public class Trie {
|
public class Trie {
|
||||||
|
|
||||||
|
private TrieConfig trieConfig;
|
||||||
|
|
||||||
private State rootState;
|
private State rootState;
|
||||||
|
|
||||||
private boolean failureStatesConstructed = false;
|
private boolean failureStatesConstructed = false;
|
||||||
|
|
||||||
public Trie() {
|
public Trie(TrieConfig trieConfig) {
|
||||||
|
this.trieConfig = trieConfig;
|
||||||
this.rootState = new State();
|
this.rootState = new State();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Trie() {
|
||||||
|
this(new TrieConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Trie removeOverlaps() {
|
||||||
|
this.trieConfig.setAllowOverlaps(false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void addKeyword(String keyword) {
|
public void addKeyword(String keyword) {
|
||||||
|
|
||||||
State currentState = this.rootState;
|
State currentState = this.rootState;
|
||||||
@ -30,6 +45,7 @@ public class Trie {
|
|||||||
currentState.addEmit(keyword);
|
currentState.addEmit(keyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Collection<Emit> parseText(String text) {
|
public Collection<Emit> parseText(String text) {
|
||||||
checkForConstructedFailureStates();
|
checkForConstructedFailureStates();
|
||||||
|
|
||||||
@ -41,6 +57,12 @@ public class Trie {
|
|||||||
storeEmits(position, currentState, collectedEmits);
|
storeEmits(position, currentState, collectedEmits);
|
||||||
position++;
|
position++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!trieConfig.isAllowOverlaps()) {
|
||||||
|
IntervalTree intervalTree = new IntervalTree((List<Intervalable>)(List<?>)collectedEmits);
|
||||||
|
intervalTree.removeOverlaps((List<Intervalable>) (List<?>) collectedEmits);
|
||||||
|
}
|
||||||
|
|
||||||
return collectedEmits;
|
return collectedEmits;
|
||||||
}
|
}
|
||||||
|
|
||||||
15
src/main/java/org/ahocorasick/trie/TrieConfig.java
Normal file
15
src/main/java/org/ahocorasick/trie/TrieConfig.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package org.ahocorasick.trie;
|
||||||
|
|
||||||
|
public class TrieConfig {
|
||||||
|
|
||||||
|
private boolean allowOverlaps = true;
|
||||||
|
|
||||||
|
public boolean isAllowOverlaps() {
|
||||||
|
return allowOverlaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowOverlaps(boolean allowOverlaps) {
|
||||||
|
this.allowOverlaps = allowOverlaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
src/test/java/org/ahocorasick/interval/IntervalTest.java
Normal file
57
src/test/java/org/ahocorasick/interval/IntervalTest.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class IntervalTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void construct() {
|
||||||
|
Interval i = new Interval(1,3);
|
||||||
|
assertEquals(1, i.getStart());
|
||||||
|
assertEquals(3, i.getEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void size() {
|
||||||
|
assertEquals(3, new Interval(0,2).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void intervaloverlaps() {
|
||||||
|
assertTrue(new Interval(1,3).overlapsWith(new Interval(2,4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void intervalDoesNotOverlap() {
|
||||||
|
assertFalse(new Interval(1, 13).overlapsWith(new Interval(27, 42)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pointOverlaps() {
|
||||||
|
assertTrue(new Interval(1,3).overlapsWith(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pointDoesNotOverlap() {
|
||||||
|
assertFalse(new Interval(1, 13).overlapsWith(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void comparable() {
|
||||||
|
Set<Interval> intervals = new TreeSet<Interval>();
|
||||||
|
intervals.add(new Interval(4, 6));
|
||||||
|
intervals.add(new Interval(2, 7));
|
||||||
|
intervals.add(new Interval(3, 4));
|
||||||
|
Iterator<Interval> it = intervals.iterator();
|
||||||
|
assertEquals(2, it.next().getStart());
|
||||||
|
assertEquals(3, it.next().getStart());
|
||||||
|
assertEquals(4, it.next().getStart());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
51
src/test/java/org/ahocorasick/interval/IntervalTreeTest.java
Normal file
51
src/test/java/org/ahocorasick/interval/IntervalTreeTest.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class IntervalTreeTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findOverlaps() {
|
||||||
|
List<Intervalable> intervals = new ArrayList<Intervalable>();
|
||||||
|
intervals.add(new Interval(0, 2));
|
||||||
|
intervals.add(new Interval(1, 3));
|
||||||
|
intervals.add(new Interval(2, 4));
|
||||||
|
intervals.add(new Interval(3, 5));
|
||||||
|
intervals.add(new Interval(4, 6));
|
||||||
|
intervals.add(new Interval(5, 7));
|
||||||
|
IntervalTree intervalTree = new IntervalTree(intervals);
|
||||||
|
List<Intervalable> overlaps = intervalTree.findOverlaps(new Interval(1,3));
|
||||||
|
assertEquals(3, overlaps.size());
|
||||||
|
Iterator<Intervalable> overlapsIt = overlaps.iterator();
|
||||||
|
assertOverlap(overlapsIt.next(), 2, 4);
|
||||||
|
assertOverlap(overlapsIt.next(), 3, 5);
|
||||||
|
assertOverlap(overlapsIt.next(), 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void removeOverlaps() {
|
||||||
|
List<Intervalable> intervals = new ArrayList<Intervalable>();
|
||||||
|
intervals.add(new Interval(0, 2));
|
||||||
|
intervals.add(new Interval(4, 5));
|
||||||
|
intervals.add(new Interval(2, 10));
|
||||||
|
intervals.add(new Interval(6, 13));
|
||||||
|
intervals.add(new Interval(9, 15));
|
||||||
|
intervals.add(new Interval(12, 16));
|
||||||
|
IntervalTree intervalTree = new IntervalTree(intervals);
|
||||||
|
intervals = intervalTree.removeOverlaps(intervals);
|
||||||
|
assertEquals(2, intervals.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertOverlap(Intervalable interval, int expectedStart, int expectedEnd) {
|
||||||
|
assertEquals(expectedStart, interval.getStart());
|
||||||
|
assertEquals(expectedEnd, interval.getEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class IntervalableComparatorByPositionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sortOnPosition() {
|
||||||
|
List<Intervalable> intervals = new ArrayList<Intervalable>();
|
||||||
|
intervals.add(new Interval(4,5));
|
||||||
|
intervals.add(new Interval(1,4));
|
||||||
|
intervals.add(new Interval(3,8));
|
||||||
|
Collections.sort(intervals, new IntervalableComparatorByPosition());
|
||||||
|
assertEquals(4, intervals.get(0).size());
|
||||||
|
assertEquals(6, intervals.get(1).size());
|
||||||
|
assertEquals(2, intervals.get(2).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package org.ahocorasick.interval;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class IntervalableComparatorBySizeTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sortOnSize() {
|
||||||
|
List<Intervalable> intervals = new ArrayList<Intervalable>();
|
||||||
|
intervals.add(new Interval(4,5));
|
||||||
|
intervals.add(new Interval(1,4));
|
||||||
|
intervals.add(new Interval(3,8));
|
||||||
|
Collections.sort(intervals, new IntervalableComparatorBySize());
|
||||||
|
assertEquals(6, intervals.get(0).size());
|
||||||
|
assertEquals(4, intervals.get(1).size());
|
||||||
|
assertEquals(2, intervals.get(2).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sortOnSizeThenPosition() {
|
||||||
|
List<Intervalable> intervals = new ArrayList<Intervalable>();
|
||||||
|
intervals.add(new Interval(4,7));
|
||||||
|
intervals.add(new Interval(2,5));
|
||||||
|
Collections.sort(intervals, new IntervalableComparatorBySize());
|
||||||
|
assertEquals(2, intervals.get(0).getStart());
|
||||||
|
assertEquals(4, intervals.get(1).getStart());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
src/test/java/org/ahocorasick/trie/EmitTest.java
Normal file
24
src/test/java/org/ahocorasick/trie/EmitTest.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package org.ahocorasick.trie;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static junit.framework.Assert.assertNotSame;
|
||||||
|
|
||||||
|
public class EmitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void equals() {
|
||||||
|
Emit one = new Emit(13, 42, null);
|
||||||
|
Emit two = new Emit(13, 42, null);
|
||||||
|
assertEquals(one, two);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notEquals() {
|
||||||
|
Emit one = new Emit(13, 42, null);
|
||||||
|
Emit two = new Emit(13, 43, null);
|
||||||
|
assertNotSame(one, two);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package org.ahocorasick;
|
package org.ahocorasick.trie;
|
||||||
|
|
||||||
|
import org.ahocorasick.trie.State;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static junit.framework.Assert.assertEquals;
|
||||||
@ -1,5 +1,7 @@
|
|||||||
package org.ahocorasick;
|
package org.ahocorasick.trie;
|
||||||
|
|
||||||
|
import org.ahocorasick.trie.Emit;
|
||||||
|
import org.ahocorasick.trie.Trie;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -77,6 +79,35 @@ public class TrieTest {
|
|||||||
checkEmit(iterator.next(), 51, 58, "broccoli");
|
checkEmit(iterator.next(), 51, 58, "broccoli");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void longAndShortOverlappingMatch() {
|
||||||
|
Trie trie = new Trie();
|
||||||
|
trie.addKeyword("he");
|
||||||
|
trie.addKeyword("hehehehe");
|
||||||
|
Collection<Emit> emits = trie.parseText("hehehehehe");
|
||||||
|
Iterator<Emit> iterator = emits.iterator();
|
||||||
|
checkEmit(iterator.next(), 0, 1, "he");
|
||||||
|
checkEmit(iterator.next(), 2, 3, "he");
|
||||||
|
checkEmit(iterator.next(), 4, 5, "he");
|
||||||
|
checkEmit(iterator.next(), 0, 7, "hehehehe");
|
||||||
|
checkEmit(iterator.next(), 6, 7, "he");
|
||||||
|
checkEmit(iterator.next(), 2, 9, "hehehehe");
|
||||||
|
checkEmit(iterator.next(), 8, 9, "he");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nonOverlapping() {
|
||||||
|
Trie trie = new Trie().removeOverlaps();
|
||||||
|
trie.addKeyword("ab");
|
||||||
|
trie.addKeyword("cba");
|
||||||
|
trie.addKeyword("ababc");
|
||||||
|
Collection<Emit> emits = trie.parseText("ababcbab");
|
||||||
|
Iterator<Emit> iterator = emits.iterator();
|
||||||
|
// With overlaps: ab@1, ab@3, ababc@4, cba@6, ab@7
|
||||||
|
checkEmit(iterator.next(), 0, 4, "ababc");
|
||||||
|
checkEmit(iterator.next(), 6, 7, "ab");
|
||||||
|
}
|
||||||
|
|
||||||
private void checkEmit(Emit next, int expectedStart, int expectedEnd, String expectedKeyword) {
|
private void checkEmit(Emit next, int expectedStart, int expectedEnd, String expectedKeyword) {
|
||||||
assertEquals(expectedStart, next.getStart());
|
assertEquals(expectedStart, next.getStart());
|
||||||
assertEquals(expectedEnd, next.getEnd());
|
assertEquals(expectedEnd, next.getEnd());
|
||||||
Loading…
x
Reference in New Issue
Block a user