字符串前缀问题:Trie树
Trie树主要是解决字符串前缀的搜索问题,例如最短前缀、某个前缀所含的字符串数等等。这是一种为解决一类字符串问题的树,与用于查找的树结构不同的是,Trie树需要在树的边上保存数据,这样组织一棵Trie树就需要有边节点和顶点节点。
一个Trie树是多叉树,根结点为空,树的边表示一个字母,顶点保存字符串状态数据,最简单的字符串的状态就是到此顶点是否有字符串完结。例如给出两个单词car与cartoon。从根结点开始,插入一个新结点,根结点到这个结点的边表示字母c
,然后再新建结点,由上一个结点到这个新结点的边为a
,然后再新增一个结点,由上一个结点到这个结点的边为字母r
,由于单词car
后续已经结束,所以这个新结点的字符串状态应表示为"有字符串完结"
,对于cartoon,由于前3个字母car已经可以沿着树的边找到,所以不需要再新增结点,只需要对后4个字母新增结点并设置相应的边即可。
基本的Trie树包括插入(建树)和搜索。这类问题一般是将一系列的数据逐个插入到树中,建立起这一组数据的前缀信息,然后查询或者搜索相应的前缀。
具体而言,使用这样的结构作为边和顶点:
struct EdgeNode { char letter; int ToVertex; }; struct VertexNode { std::vector<EdgeNode> childs; bool EndHere; int tollStation; }; std::vector<VertexNode> Tree;
这里的VertexNode
使用vector<EdgeNode>
表示顶点所连的边,Endhere
表示是否有字符串在这里终止,tollStation
(收费站)来记录有多少个字符串经过了这个结点。Edge保存了它所表示的字母letter
和它指向的结点编号ToVertex
。
整个Trie树使用std::vector<VertexNode> Tree
表示,首先将一个空VertexNode结点插入到Tree中,作为树根Tree[0]。插入操作为:对于待插入字符串的每一个字符,首先将它经过的结点的tollStation
加一,然后再检查它是否存在相应路径能对应到这个字符,如果存在就将树继续向之后的顶点进行检查,如果顶点的所有边都不存在相应字符,则新建一个顶点,新建一条边指向这个新顶点,使这条边的字符是字符串上相应位置的字符。直到字符串读到末尾,将边的“EndHere”状态置为1.
void insertWord(char* msg) { int vid = Tree.size(); // start to root int currId = 0; for(int i=0;msg[i];i++) { bool needMakeNewNode = true; Tree[currId].tollStation ++; VertexNode currVertexNode = Tree[currId]; // all edge of this vertex for(int ei = 0; needMakeNewNode && ei<currVertexNode.childs.size(); ei++) { // if the letter of this edge == msg[i] // move vertex to this edge pointing. if(msg[i] == currVertexNode.childs[ei].letter) { currId = currVertexNode.childs[ei].ToVertex; needMakeNewNode = false; break; } } // if no letters of edges equal the msg[i] // then, make a new edge and new vertex if(needMakeNewNode) { EdgeNode e; e.letter = msg[i]; e.ToVertex = vid; Tree[currId].childs.push_back(e); VertexNode v; v.EndHere = false; v.tollStation = 0; Tree.push_back(v); currId = vid; vid++; } } // msg read over, set end = true; Tree[currId].tollStation ++; Tree[currId].EndHere = true; }
另一项操作为搜索,给出一个字符串,判断以它作为前缀的单词数量。有些情况下要考虑这个前缀并不存在树中。如果给出的前缀字符串能在树中找到,我们输出它的tollStation值即可,如果不在树中则输出0.
int search(char* msg) { int currId = 0; int InTree = true; int ei; for(int i=0; InTree && msg[i];i++) { VertexNode currVertexNode = Tree[currId]; for(ei = 0; ei<currVertexNode.childs.size(); ei++) { EdgeNode e = currVertexNode.childs[ei]; if(e.letter==msg[i]) { currId = e.ToVertex; break; } } if(ei==currVertexNode.childs.size()) { InTree = false; } } if(InTree) { return Tree[currId].tollStation; } else { return 0; } }
与Trie树相关的两题: