我正在尝试在Java中实现后缀trie。 trie有一个根节点,并连接到它是边缘。但是,当实现诸如constructTrie(T)
(构造给定字符串T的trie)或substring(S,T)
(检查S是否是T的子字符串)之类的函数时,我保持当前节点cNode
整个代码的变化取决于我正在考虑的节点。
我不确定我是否正确更改了cNode
的值。以下是课程Trie
。
import java.util.*;
class Trie{
protected Node root = null;
public Trie(){
Node n = new Node();
root = n;
}
// Constructs a trie for a given string T
public void constructTrie(String T){
ArrayList<String> suffixArray = new ArrayList<String>();
T += "#"; // Terminator
int length = T.length();
// Creates suffix array and removes first letter with every iteration
for(int i=0; i<length; i++){
suffixArray.add(T);
T = T.substring(1);
}
// Goes through suffix array
for(int i=0; i<length; i++){
Node cNode = null;
cNode = root; // Current node
int j = 0;
// Goes through each letter of an entry in the suffix array
while(j < (suffixArray.get(i)).length()){
int index = cNode.findEdge((suffixArray.get(i)).charAt(j));
// If an edge is found at the root with the current letter, update cNode and remove the letter from word
if(index != -1){
cNode = cNode.getEdge(index).getNode(); // Gets node pointed at by edge and sets it as current node
String replace = (suffixArray.get(i)).substring(1);
suffixArray.set(0, replace); // Erases first letter of suffix
j++;
System.out.println(i + " " + j + " " + replace);
}
// If an edge is not found at the root, write the whole word
else{
for(int k=0; k<(suffixArray.get(i)).length(); k++){
Edge e = new Edge((suffixArray.get(i)).charAt(k)); // Creates edge with current letter of current entry of the suffix array
Node n = new Node(); // Creates node to be pointed at by edge
e.setNode(n);
cNode.newEdge(e);
cNode = n; // Updates current node
}
j = (suffixArray.get(i)).length(); // If the word is written, we break from the while and move on to the next suffix array entry
}
}
}
}
// Checks if S is a substring of T
public boolean substring(String S, String T){
constructTrie(T);
Node cNode = null;
cNode = root;
int index;
for(int i=0; i<S.length(); i++){
index = cNode.findEdge(S.charAt(i));
if(index == -1)
return false; // Substring was not found because a path was not followed
cNode = (cNode.getEdge(index)).getNode(); // Reset current node to the next node in the path
}
return true; // Substring was found
}
具体来说,我是否允许将Node root = null
设置为类变量,在创建Trie类型的对象时初始化root
并更改cNode
,如方法中所示?代码编译时没有错误,但是在测试时它并不总是输出正确的响应,例如在测试时,它输出“&#39; es&#39;不是&#39;害虫的子串。
答案 0 :(得分:3)
更新类方法中的字段会使类不是线程安全的。您的方法有副作用,可能不是您班级用户所期望的。
考虑:
Trie t = new Trie("My String");
boolean startsWithMy = t.substring("My");
boolean startsWithMyString = t.substring("My String");
如果您正在更新substring方法中的root
字段,那么第二次调用将无法执行您所期望的操作,因为第一个子字符串调用更改了Trie。
如果你想创建一个易于使用且副作用最小的可重用类,那么我要做的就是按照这个基本模式编写你的类:
public class Trie {
private final Node root;
public Trie(String input) {
// Construct the Trie here and assign it to root:
this.root = constructTry(input);
}
public boolean substring(String part) {
// Create a local Node variable:
Node currentNode = root;
// Navigate the Trie here using currentNode:
// ...
return result;
}
}
您甚至可以添加一个方法(如果您愿意)返回Trie的子部分:
public Trie subTrie(String part) {
// Find the Node here that matches the substring part, and return it.
// If nothing found, then throw NoSuchElementException or return null.
Node subNode = findNode(part);
if (subNode == null) {
throw new NoSuchElementException("No element starting with: " + part);
}
// Constructs a new Trie with a different root node using a 2nd constructor option
return new Trie(subNode);
}
答案 1 :(得分:1)
您正在通过向其添加垃圾来更改根节点的引用。 假设你这样做:
Trie trie = new Trie();
trie.substring("es", "pest"); // this returns true.
但如果你这样做
Trie trie = new Trie();
trie.substring("es", "pest");
trie.substring("te", "Master");
您对Substring的第二次调用将在您上次呼叫离开的地方继续。你的root已经被初始化了,并且包含了一个词&#34; pest&#34; root(p, e, s, t, #)
。在第二次通话后,而不是按预期root(M, a, s, t, e, r, #)
,您最终得到root(p, e, s, t, #, M, a, r)
。这是一个完全不同的词。因此te
不是pest#Mar
的子字符串。
但如果按照@ john16384实施,你将被迫做以下事情,以消除副作用:
Trie trie = new Trie("pest");
trie.substring("es"); // this returns true.
trie = new Trie("Master");
trie.substring("te") // this returns true.
这样做总是迫使你从一个干净的根开始。请参阅以下实施:
class Trie {
protected Node root = null;
public Trie(String T) {
root = constructTrie(T);
}
// Constructs a trie for a given string T
private Node constructTrie(String T) {
ArrayList<String> suffixArray = new ArrayList<String>();
T += "#"; // Terminator
int length = T.length();
// Creates suffix array and removes first letter with every iteration
for (int i = 0; i < length; i++) {
suffixArray.add(T);
T = T.substring(1);
}
Node localRoot = new Node();
// Goes through suffix array
for (int i = 0; i < length; i++) {
Node cNode = localRoot;
int j = 0;
// Goes through each letter of an entry in the suffix array
while (j < (suffixArray.get(i)).length()) {
int index = cNode.findEdge((suffixArray.get(i)).charAt(j));
// If an edge is found at the root with the current letter, update cNode and remove the letter from word
if (index != -1) {
cNode = cNode.getEdge(index).getNode(); // Gets node pointed at by edge and sets it as current node
String replace = (suffixArray.get(i)).substring(1);
suffixArray.set(0, replace); // Erases first letter of suffix
j++;
System.out.println(i + " " + j + " " + replace);
}
// If an edge is not found at the root, write the whole word
else {
for (int k = 0; k < (suffixArray.get(i)).length(); k++) {
Edge e = new Edge((suffixArray.get(i)).charAt(k)); // Creates edge with current letter of current entry of the suffix array
Node n = new Node(); // Creates node to be pointed at by edge
e.setNode(n);
cNode.newEdge(e);
cNode = n; // Updates current node
}
j = (suffixArray.get(i)).length(); // If the word is written, we break from the while and move on to the next suffix array entry
}
}
}
return localRoot;
}
// Checks if S is a substring of T
public boolean substring(String S) {
Node cNode = root;
int index;
for (int i = 0; i < S.length(); i++) {
index = cNode.findEdge(S.charAt(i));
if (index == -1)
return false; // Substring was not found because a path was not followed
cNode = (cNode.getEdge(index)).getNode(); // Reset current node to the next node in the path
}
return true; // Substring was found
}
}