后缀树代码

  1 #include <cstring>
  2 #include <list>
  3 #include <vector>
  4 #include <cstdio>
  5 using namespace std;
  6 
  7 //后缀树中的结点类 
  8 struct SfxNode
  9 {
 10     const char *l,*r;        // 指向字符数组的指针 ,表示其父边标记为子串[l,r) 
 11     SfxNode * sfxLink;        //后缀链,指向另一个结点 
 12     list<int> from;            //由于拓展到多串情形,所以需要记录在此结尾的串列表 
 13     SfxNode* father;        //在后缀树中的父亲结点 
 14     SfxNode* firstCh;        //在后缀树中第一个孩子结点(如果没有,那么为叶节点) 
 15     SfxNode* left;            //左兄弟,即父亲的孩子列表中排在该结点前一个的结点 
 16     SfxNode* right;            //右兄弟,与上面类似 
 17     
 18     //获取当前结点的孩子中,边标记以c开始的那个孩子 
 19     SfxNode* child(char c) const
 20     {
 21         SfxNode* p = NULL;
 22         if(firstCh && *firstCh->l <= c)
 23             p = firstCh;
 24         else
 25             return p;        //不存在,直接返回空指针 
 26         while(p->right && *p->right->l <= c)
 27             p = p->right;
 28         return p;            //找到编号小于等于c的最大孩子(如果有编号为c就返回该 
 29     }                        //孩子,否则返回将要插入位置的左兄弟) 
 30     
 31     //添加新的子结点add. p为add的左兄弟 
 32     void addChild(SfxNode* p,SfxNode* add)
 33     {
 34         if(p)                //如果左兄弟存在 
 35         {
 36             add->right = p->right;
 37             add->left = p;
 38             p->right = add;
 39             if(add->right)
 40                 add->right->left = add;    //一系列链表维护的基本操作 
 41         }
 42         else            //否则为第一个孩子结点 
 43         {
 44             add->left = NULL;
 45             add->right = firstCh;
 46             firstCh = add;
 47             if(add->right)
 48                 add->right->left = add;
 49         }
 50         add->father = this;        //更新父亲结点 
 51     }
 52 };
 53 
 54 
 55 SfxNode sfxNodePool[400000 + 100];        //后缀结点的手工内存池(考虑到效率和安全) 
 56 int sfxNodeCnt = 0;                        //当前树中结点个数 
 57 
 58 struct SuffixTree
 59 {
 60     SfxNode* root;        //根节点    
 61                     
 62     SuffixTree():root(newNode()),texts(),textLens()
 63     {
 64         
 65     }
 66     ~SuffixTree()        //构造和析构函数 
 67     {
 68         clear();
 69     }
 70     
 71     //接text所有后缀加入到后缀树中(字符数组以'\\0'结尾,所以正好将其作为终止符$) 
 72     void addText(const char * text)
 73     {
 74         //一些初始化工作,变量定义见该类的private部分 
 75         curText = curPos = text;
 76         lastAddPos = 0;
 77         activeNode = root;
 78         root->l = root->r = curText;
 79         curTextLen = strlen(text);
 80         for(int i = 0;i <= curTextLen;i++) //i个阶段 
 81         {
 82             newlyAddNode = NULL;
 83             //每个阶段的扩展。依据单调性从上次最后一次扩展开始 
 84             for(int j = lastAddPos;j <= i;j++)
 85             //如果扩展不成功(即满足扩展规则3,那么终止本阶段之后所有扩展) 
 86                 if(!extend(curText + j,curText + i))
 87                     break;
 88         }
 89         //将插入的字符串备份 
 90         texts.push_back(curText);
 91         textLens.push_back(curTextLen);
 92     }
 93     
 94     //清理工作 
 95     void clear()
 96     {
 97         sfxNodeCnt = 0;
 98         root = newNode();
 99         texts.clear();
100         textLens.clear();
101     }
102     
103     //为了遍历后缀树的临时数组 
104     char str[10000];
105     
106     //遍历后缀树,供测试用 
107     void travel(SfxNode* curNode,int curindex)
108     {
109         for(const char* i = curNode->l;i != curNode->r;++i)
110             str[curindex++] = *i;
111         if(curNode->firstCh)
112         {
113             for(SfxNode* node = curNode->firstCh;node;node = node->right)
114                 travel(node,curindex);
115         }
116         else
117             printf("%s\n",str);
118     }
119     
120 private:
121     //用来备份插入的字符串 
122     vector<const char*> texts;
123     vector<int> textLens;
124     SfxNode * activeNode,* newlyAddNode;//当前被激活的结点和新添加的结点
125     //每次扩展的时候即从被激活的结点开始向下走;记录新添加的结点的原因是为了
126     //维护它的后缀链(回想一下新建结点后缀链的添加时在下次扩展的时候) 
127     const char * curPos;    //当前扩展的位置 
128     const char * curText;    //插入的字符数组的指针 
129     int lastAddPos;            //上阶段最后扩展的位置,利用其单调性保证算法线性 
130     int curTextLen;            //插入到后缀树中的字符串的长度 
131     
132     //新建一个结点,手工实现内存管理 
133     SfxNode* newNode(const char* l = NULL,const char* r = NULL)
134     {
135         SfxNode *p = &sfxNodePool[sfxNodeCnt++];
136         p->l = l;
137         p->r = r;        //设置新建结点的父亲边标记 
138         p->from.clear();
139         p->sfxLink = p->father = p->firstCh = p->left = p->right = NULL;
140         return p;
141     }
142     
143     //从被激活的结点开始向下走直道找到要找的字符串[l,r) 
144     void goDown(const char * l,const char * r)
145     {
146         curPos = activeNode->r;
147         while(l < r)
148         {
149             activeNode = activeNode->child(*l);
150             if(r - l <= activeNode->r - activeNode->l)//如果要找的位置就在该边内 
151             {
152                 curPos = activeNode->l + (r - l);
153                 return;
154             }
155             else        //否则可以跨过该条边继续处理 
156             {
157                 curPos = activeNode->r;
158                 l += activeNode->r - activeNode->l;
159             }
160         }
161     }
162     
163     //扩展,将子串[i,r]插入到隐式树中,返回是否插入成功(即是否不满足扩展规则3) 
164     bool extend(const char* i,const char * r)
165     {
166         if(curPos < activeNode->r)
167         {
168             const char* l;
169             if(*curPos == *r)
170             {
171                 if(*r)    //如果不是最后一次扩展,那么当前就已经扩展完了 
172                 {
173                     curPos++;
174                     return false;
175                 }
176                 activeNode->from.push_back(texts.size());    //记录位置 
177                 l = r - (activeNode->r - activeNode->l - 1);
178             }
179             else
180             {
181                 SfxNode * in = newNode(activeNode->l,curPos);    //新建中间结点
182                 //新建的结点用来替代当前被激活结点的父亲结点 
183                 in->left = activeNode->left;
184                 in->right = activeNode->right;
185                 in->father = activeNode->father;
186                 if(activeNode->left)
187                     activeNode->left->right = in;
188                 if(activeNode->right) 
189                     activeNode->right->left = in;
190                 if(activeNode->father->firstCh == activeNode)
191                     activeNode->father->firstCh = in;
192                 in->addChild(NULL,activeNode);    //以上操作维护树边及兄弟指针 
193                 activeNode->l = curPos;
194                 //应用扩展规则2,新建叶结点 
195                 SfxNode * leaf = newNode(r,curText + curTextLen + 1);
196                 in->addChild(in->child(*r),leaf);
197                 lastAddPos++;
198                 leaf->from.push_back(texts.size());
199                 //维护后缀链 
200                 if(newlyAddNode)
201                     newlyAddNode->sfxLink = in;
202                 activeNode = newlyAddNode = in;
203                 l = r - (activeNode->r - activeNode->l);
204             }
205             
206             activeNode = activeNode->father;
207             if(activeNode->sfxLink)
208                 activeNode = activeNode->sfxLink;
209             else
210                 l++;
211             goDown(l,r);    //向下走找到下次扩展的位置,即更新激活结点 
212             
213         }
214         else        //终止在节点处而不是边内部 
215         {
216             if(newlyAddNode)
217             {
218                 newlyAddNode->sfxLink = activeNode;
219                 newlyAddNode = NULL;
220             }
221             SfxNode* ch = activeNode->child(*r);
222             if(ch && *ch->l == *r)
223             {
224                 if(*r)        //如果不是最后一次扩展,那么当前就已经扩展完了 
225                 {
226                     activeNode = ch;
227                     curPos = activeNode->l + 1;
228                     return false;
229                 }
230                 ch->from.push_back(texts.size());
231             }
232             else        //没有找到该孩子,应用规则2,新建叶结点 
233             {
234                 SfxNode * leaf = newNode(r,curText + curTextLen + 1);
235                 activeNode->addChild(ch,leaf);
236                 lastAddPos++;
237                 leaf->from.push_back(texts.size());
238             }
239             if(i < r)
240             {
241                 activeNode = activeNode->sfxLink;
242                 curPos = activeNode->r;
243             }
244         }
245         return true;
246             
247     }
248 };
249 
250 //一个用来测试的主程序,添加字符串“xabxac” 后输出后缀树中所用后缀 
251 SuffixTree tree;
252 int main()
253 {
254     tree.addText("xabxac");
255     tree.travel(tree.root,0);
256     return 0;
257 }
258 
259 
260 /*
261 abxac
262 ac
263 bxac
264 c
265 xabxac
266 xac
267 */ 

 

posted @ 2022-03-11 00:00  互联星空  阅读(79)  评论(0编辑  收藏  举报