用Trie(也叫前缀树)来保存字符串集合。
每个单词的结束位置对应一个“单词结点”。反过来,从根节点到每个单词结点的路径上所有字母连接而成的字符串就是该结点对应的字符串,在程序上,将根节点编号成0,然后把其余结点编号为从1开始的正整数,然后用一个数组来保存每个结点的所有子节点,用下标直接存取。
具体来说,可以用ch[i][j]保存结点i的那个编号为j的子结点。什么叫编号为j呢?比如,若是处理全部由小写字母组成的字符串,把所有小写字母按照字典序编号为0,1,2,….,则ch[i][0]表示结点i的子节点a。如果这个子节点不存在,则ch[i][0]=0。用sigma_size表示字符集的大小,比如,当字符集为全体小写字母时,sigma_size=26.
使用trie时,往往需要在单词结点上附加信息,其中val[i]表示结点i对应的附加信息。例如,如果每个字符串有一个权值,就可以把这个权值保存在val[i]中。简单起见,假定权值大于0,因此val[i]>0当且仅当结点i是单词结点。
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-'a';
}
void insert(char *s,int v){
int u=0,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++;
}
u=ch[u][c];
}
val[u]=v;
}
void find_prefixes(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]);
}
}
}
};
Trie trie;