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 */