上图就是一个前缀树,又称字典树。表示abcd,abd,b,bcd,efg,hig六个单词。从根节点到每个单词节点的路径上所有字母连接而成的字符串就是该节点对应的字符串。
在程序上,将根节点编号为0,然后把其他节点编号为1开始的正整数,然后用一个数组来保存每个节点的所有子节点,用下标直接存取。
具体说就是,可以用ch[i][j]保存节点i的那个编号为j的子节点。我们用一个编号代表一个字符,这样就可以判断每条边上的字母是什么了。
使用Trie的时候,往往需要在单词节点上附加信息,其中val[i]表示节点i对应的附加信息。
我的模板://maxnode 为节点数,sigma_size为边的最多个数
struct Trie{
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
Trie(){sz=1;memset(ch[0],0,sizeof(ch[0])););}//初始时只有一个根节点;
int idx(char c){return c-'a';} //字符c的编号;
//插入字符串s,附加信息为v,注意v必须非0,因为0代表“本节点不是单词节点”
int 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; //中间节点的附加信息为0
ch[u][c]=sz++; //新建节点
}
u=ch[u][c]; //往下走
}
val[u]=v;
return 0;
}
};
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
Trie(){sz=1;memset(ch[0],0,sizeof(ch[0])););}//初始时只有一个根节点;
int idx(char c){return c-'a';} //字符c的编号;
//插入字符串s,附加信息为v,注意v必须非0,因为0代表“本节点不是单词节点”
int 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; //中间节点的附加信息为0
ch[u][c]=sz++; //新建节点
}
u=ch[u][c]; //往下走
}
val[u]=v;
return 0;
}
};
由于不同题目问题不同,查找方式也大不相同,我就不列出查找的代码了。
一道例题:uva 1401
分析题目可得到递推方程d(i)=sum{d(i+len(x))|单词x是s[i...L]的前缀}
但是由于单词个数过多,所有我们要建立trie模型,提高效率,节省时间。
下面是我的代码:
#include <cstdio>
#include <cstring>
#include <vector>
const int maxnode =4000 * 100 + 10;
const int sigma_size=26;
using namespace std;
struct Trie{
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
void clears(){sz=1;memset(ch[0],0,sizeof(ch[0]));}
int idx(char c){return c-'a';}
void inserts(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;
}
int finds(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]);
}
return 0;
}
};
char s[300010];
int len[4005];
struct Trie tree;
int main (){
char s0[105];
int d[300010],kase=0;
int n;
while (scanf("%s",s)!=EOF){
tree.clears();
scanf("%d",&n);
for (int i=1;i<=n;i++) {
scanf("%s",s0);
len[i] = strlen(s0);
tree.inserts(s0,i);
}
memset(d, 0, sizeof(d));
int m=strlen(s);d[m]=1;
for (int i=m-1;i>=0;i--){
vector<int> p;
tree.finds(s+i, m-i, p);
for(int j = 0; j < p.size(); j++)
d[i] = (d[i] + d[i+len[p[j]]]) % 20071027;
}
printf("Case %d: %d\n",++kase,d[0]);
}
return 0;
}
#include <cstring>
#include <vector>
const int maxnode =4000 * 100 + 10;
const int sigma_size=26;
using namespace std;
struct Trie{
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
void clears(){sz=1;memset(ch[0],0,sizeof(ch[0]));}
int idx(char c){return c-'a';}
void inserts(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;
}
int finds(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]);
}
return 0;
}
};
char s[300010];
int len[4005];
struct Trie tree;
int main (){
char s0[105];
int d[300010],kase=0;
int n;
while (scanf("%s",s)!=EOF){
tree.clears();
scanf("%d",&n);
for (int i=1;i<=n;i++) {
scanf("%s",s0);
len[i] = strlen(s0);
tree.inserts(s0,i);
}
memset(d, 0, sizeof(d));
int m=strlen(s);d[m]=1;
for (int i=m-1;i>=0;i--){
vector<int> p;
tree.finds(s+i, m-i, p);
for(int j = 0; j < p.size(); j++)
d[i] = (d[i] + d[i+len[p[j]]]) % 20071027;
}
printf("Case %d: %d\n",++kase,d[0]);
}
return 0;
}