字典树模板
老久没写博客了,跟一波风。
去年暑假集训的时候学的字典树,ac自动机什么的,用的不是很多,该忘还是得忘,毕竟属于数据结构(我也想全面developing啊),我专精的图论有空再发嘛
寒假集训开始,打算先复习一下以前学的各种算法,然后再去专研图论的东西(学了一堆,脑子里面却已经忘得零零散散,再去学新东西着实顶不住那零散的阴影)
还好,当初学的很认真,现在简单回想复习一下,懂还是没问题的,内化为自己的理解。
示例模板题:http://www.fjutacm.com/Problem.jsp?pid=2050
自己的理解融于代码之中了,另外也推荐一篇别人写的(我就是馋人家的好图懒得搬):https://blog.csdn.net/weixin_39778570/article/details/81990417
#include<cstdio> #include<cstring> #define N 100009 #define mt(x) memset(x,0,sizeof x) char s[N][30],S[30];//存字符串 int tree[N*5][30];//字典树 int index[N*5],num,cnt;//下标辅助 /* index用于 指出根节点所示的字符串 在s字符串数组中的下标 cnt 用于辅组index依序储存根节点 num 用于指向在tree中新的根节点所示值 也就是说: 创建tree时,根节点对应++num去标记, 完整字符串对应index中cnt去标记 (也就是在index数组中,特定的num值转存下标为++cnt,表示这里是第cnt+1个字符串) 然后,要在s字符串数组中找指定字符串 首先,查找完整字符串的cnt下标,也就是num的值,原字符串遍历找到对应num 得到num之后,我们已经用index数组标记了num所示的字符串在s字符串数组中的下标为cnt 也即, s[index[num]],就是我们要找的字符串 */ void Init() { mt(tree);mt(s);mt(index); num=0;cnt=0; } void Insert() { int t=0;//根结点值 int i=0,k=0;//区分两字符串 while(S[i++]!=' '); k=i;//标记区分下标,k指向value字符串第一个字符 while(S[i]) { int id=S[i++]-'a'; if(!tree[t][id])tree[t][id]=++num;//不存在,则创建分支,标记为++num t=tree[t][id];//转移结点 } index[t]=++cnt; strncpy(s[cnt],S,k-1);//复制字符串 /* for(int i=0;i<k-1;++i) s[cnt][i]=S[i]; //*/ } int Find() { int t=0,i=0; while(S[i]) { int id=S[i++]-'a'; if(!tree[t][id])return 0; t=tree[t][id]; } return t; } int main() { Init(); while(gets(S)&&S[0]) Insert(); while(~scanf("%s",S)) { int i=Find(); printf("%s\n",i?s[index[i]]:"eh"); } return 0; }
另外,因为是两两匹配,很容易让人想到STL里的一个关联容器Map,在题目数据很水或者时限不小的情况也可以这么写,这题也行。
(两种的解法时间是倍数关系,字典树tql)
#include<iostream> #include<cstdio> #include<map> using namespace std; int main() { map<string,string>p; char x[30],y[30]; while(gets(x)&&x[0]) { sscanf(x,"%s%s",y,x); p[x]=y; } while(gets(x)) cout<<(p[x]==""?"eh":p[x])<<endl; return 0; }
另外,复习的时候想到了,通过另外一个数组来标识一个数组的下标这种写法很常见,类似索引的意思
最经典的就是链式前向星存图,里面的head数组与num搭配,指定边的下标
好处的话,能理解桶排的妙处都能理解这样写的好处吧,字典树这里面也用到了,将字典树里的结点值存在一个自增数组里,再用自增值取指向结果字符串数组中的下标
也就这么个转换关系,初次理解可能会觉得有点绕,但真的不难,也就【x->三元关系->y】这种意思。

浙公网安备 33010602011771号