后缀树代码
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 */