前缀树里面可以存一堆字符串,也可以说是一堆单词,存完之后我们可以轻松判断一个指定的字符串是否出现过
下面我来详细解释一下实现细节
const int maxnode=10000*10+10; //单词个数*每一个单词的字符数 const int sigma_size=10; struct Trie { int ch[maxnode][sigma_size]; int val[maxnode]; int sz; void clear() { sz=1; memset(ch[0],0,sizeof(ch[0])); memset(val,0,sizeof(val)); } int idx(char c) { return c-'0'; //这里根据情况修改 } };
这里面的结点总数为单词个数和每一个单词最多包含的字符数的乘积。
sigma_size是单词字符的字符集大小,数字就是10,字母就是26
看ch之前我们先来理解一下sz,sz是一个自增变量,是每一个结点的唯一标志,也就是ID
ch[i][j]就表示的是第i个结点是第j种字符表示的(很显然sigma_size作为字符集大小),其值为当前结点在创建时分配的ID,也就是sz
idx这个函数是将字符转化成对应的下标的,也就是前面提到的那个j,第j种字符
每一个结点都可以有一个附加信息,附加什么都可以,但是如果这个结点有意义不要附加0,因为附加0的话表示当前结点没意义(*^▽^*)
那我附加什么好呢,我可以附加这个单词在单词数组中的下标,这样我就能直接根据这个附加信息查到这个单词是啥了不用遍历了
接下来我们介绍插入部分
void insert(const char *s,int v) //v是每一个单词的附加信息,一定不能是0 { int u=0; int n=strlen(s); for(int i=0;i<n;i++) { int c=idx(s[i]); if(!ch[u][c]) { memset(ch[sz],0,sizeof(ch[sz])); val[sz]=0; ch[u][c]=sz++; } //这里可以加else改为int函数,注意一定要等输入完再break否则运行错 /* else { if(val[ch[u][c]]) return 0; if(i==n-1) return 0; } */ u=ch[u][c]; } val[u]=v; }
插入部分的核心就是根据字符串创建ch并分配sz,到底了之后,给这个结点val赋值表示单词结束。
注释部分可以直接把插入改成有返回值的函数,如果之前这个词插过了就不插入了返回0,这里分两种情况,一种是插入的本身就是一个前缀,一种是之前的单词是这次新插入单词的前缀。
void find_prefixes(const char *s,int len,vector<int>& ans) { int u=0; for(int i=0;i<len;i++) { if(s[i]=='\0') break; int c=idx(s[i]); if(!ch[u][c]) break; u=ch[u][c]; if(val[u]!=0) ans.push_back(val[u]); } }
这个函数会在整个树中找当前前缀的所有单词并把他们的标记存在ans数组里,比如前缀是ai,那么aie,air,aiop等的标记就都找到了,相当于这些单词都找到了。
完整代码如下,实现了一个动态的插入和判断当前插入的单词是否是已经插入的单词前缀的过程(两种情况哦)。
1 #include<iostream> 2 #include<cstring> 3 #include<vector> 4 using namespace std; 5 const int maxnode=10000*10+10; //单词个数*每一个单词的字符数 6 const int sigma_size=10; 7 struct Trie 8 { 9 int ch[maxnode][sigma_size]; 10 int val[maxnode]; 11 int sz; 12 void clear() 13 { 14 sz=1; 15 memset(ch[0],0,sizeof(ch[0])); 16 memset(val,0,sizeof(val)); 17 } 18 int idx(char c) 19 { 20 return c-'0'; //这里根据情况修改 21 } 22 void insert(const char *s,int v) //v是每一个单词的附加信息,一定不能是0 23 { 24 int u=0; 25 int n=strlen(s); 26 for(int i=0;i<n;i++) 27 { 28 int c=idx(s[i]); 29 if(!ch[u][c]) 30 { 31 memset(ch[sz],0,sizeof(ch[sz])); 32 val[sz]=0; 33 ch[u][c]=sz++; 34 } 35 //这里可以加else改为int函数,注意一定要等输入完再break否则运行错 36 /* 37 else 38 { 39 if(val[ch[u][c]]) 40 return 0; 41 if(i==n-1) 42 return 0; 43 } 44 */ 45 u=ch[u][c]; 46 } 47 val[u]=v; 48 } 49 void find_prefixes(const char *s,int len,vector<int>& ans) 50 { 51 int u=0; 52 for(int i=0;i<len;i++) 53 { 54 if(s[i]=='\0') 55 break; 56 int c=idx(s[i]); 57 if(!ch[u][c]) 58 break; 59 u=ch[u][c]; 60 if(val[u]!=0) 61 ans.push_back(val[u]); 62 } 63 } 64 }; 65 const int maxn=10005; 66 int n; 67 Trie trie; 68 char word[maxn][15]; 69 int main() 70 { 71 int T; 72 cin>>T; 73 while(T--) 74 { 75 trie.clear(); 76 int flag=1; 77 cin>>n; 78 for(int i=0;i<n;i++) 79 { 80 cin>>word[i]; 81 trie.insert(word[i],i+1); 82 } 83 vector<int> p; 84 for(int i=0;i<n;i++) 85 { 86 p.clear(); 87 trie.find_prefixes(word[i],strlen(word[i])-1,p); 88 if(p.size()!=0) 89 { 90 flag=0; 91 break; 92 } 93 } 94 if(flag) 95 cout<<"YES"<<endl; 96 else 97 cout<<"NO"<<endl; 98 } 99 return 0; 100 }
ch[maxnode][sigma_size]