Description
Input
Output
Sample Input
2 3 911 97625999 91125426 5 113 12340 123440 12345 98346
Sample Output
NO YES
首先贴上trie树的模板,然后这一题可以根据模板改一下子就ok:
#include <iostream> #include<stdio.h> #include<string.h> using namespace std; const int NODE = 1e5+10,CH = 26; /**** INPUT: (Lower case) 输入n,m 输入n个串s1[i] 输入m个串s2[i] OUTPUT: 对于每个s2[i] 在s1[i]中查找是否存在 存在则输出所在s1[i]中的标号 否则输出0 */ struct Trie { int ch[NODE][CH],sz,val[NODE]; /** ch[u][c]: 节点u指向的c儿子的边 val[u]: 节点u的值 sz: trie树的size */ inline int idx(char c) { return c-'a'; } int node() { /** 新建(初始化)节点 **/ memset(ch[sz],0,sizeof(ch[sz])); ///将所有儿子置为空 val[sz]=0; return sz++; } void init() { sz=0; ///trie树根节点为下标0的节点 node(); } void insert(char *s,int v) { int u=0; for(;*s;s++) { int c=idx(*s); ///如果节点不存在 新建节点 并把值赋给当前节点的c儿子边 if(!ch[u][c]) ch[u][c]=node(); ///继续移动 u=ch[u][c]; } ///在末尾节点记录信息 val[u]=v; } int find(char *s) { int u=0; for(;*s;s++) { int c=idx(*s); ///如果u节点没有c儿子 结束 if(!ch[u][c]) return 0; u=ch[u][c]; } return val[u]; } }soul; char s[10000]; int main() { int n,m; soul.init(); scanf("%d%d\n",&n,&m); for(int i=1;i<=n;i++) scanf("%s",s),soul.insert(s,i); while(m--) { scanf("%s",s); printf("%d\n",soul.find(s)); } return 0; }
这题有一点点不一样的就是在原模板基础上增加了一个num数组,用这个数组来干嘛呢?当然是记录数据呀~~~~~~神马数据需要记录??这就需要深入理解trie树的深刻含义了:
这就是trie树的构建,构建完成之后会发现其实对应的每个公共节点出来的边的ch[i][j]的i都一样!一样的话这就说明求前缀是有路可寻的!因为我们虽然一个个判断前缀时间可能很慢,但是!现在发现了这个规律,那么我们把每个这种节点使用的次数记录下来,这样就可以表示经过这个节点的有多少线段,相当于一种dp思想,经过一次这个节点的数量就增加一次,最后公共前缀就显而易见的在num数组里了。而这题我们将所有数据先存,后来在一个个使用这些数据,那么如果没有前缀肯定是会走到自己的节点末端的,所以num数组求出来的值至少是1,当然,大于1便说明有更长的单词经过了这个节点,那么自然有前缀啦~~~~~
下面是这题解题代码:
#include <iostream> #include<stdio.h> #include<string.h> using namespace std; const int NODE = 1e6+10,CH = 26; int ch[NODE][CH],sz,val[NODE],num[NODE]; char a1[100001][15],a2[15]; /** ch[u][c]: 节点u指向的c儿子的边 val[u]: 节点u的值 sz: trie树的size */ int idx(char c) {return c-'0';} int node() { /** 新建(初始化)节点 **/ memset(ch[sz],0,sizeof(ch[sz])); ///将所有儿子置为空 val[sz]=0; return sz++; } void init() { sz=0; ///trie树根节点为下标0的节点 node(); } void insert(char *s,int v) { int u=0; num[u]++; for(;*s;s++) { int c=idx(*s); ///如果节点不存在 新建节点 并把值赋给当前节点的c儿子边 if(!ch[u][c]) ch[u][c]=node(); ///继续移动 // cout<<u<<' '<<char(c+'a')<<' '<<ch[u][c]<<endl; u=ch[u][c];num[u]++; } ///在末尾节点记录信息 val[u]=v; } int find(char *s) { int u=0; for(;*s;s++) { int c=idx(*s); // cout<<u<<' '<<(char)(c+'a')<<' '<<num[u]<<endl; ///如果u节点没有c儿子 结束 if(!ch[u][c]) return 0; u=ch[u][c]; // cout<<(char)*s<<endl; } return num[u]; } int main() { int t;scanf("%d",&t); while(t--) { init(); memset(num,0,sizeof(num)); int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%s",a1[i]); insert(a1[i],i+1); } int flag=1; for(int i=0;i<n;i++) { if(find(a1[i])>1) {printf("NO\n");flag=0;break;} } if(flag==1)printf("YES\n"); } return 0; }