两个星期没有刷题了,,从今天开始吧,先从hiho开始刷,巩固一下之前学的。。
Trie树专题。。
题目链接:HDU - 1075
题意:给出火星文单词对应的英文单词,输入一段火星文,输出英文。
用trie树做,节点挂上对应的中文单词。
也可以用map做。
1 #include <cstdio> 2 #include <cstring> 3 #include <cctype> 4 char a[15],b[15],s[3010]; 5 6 struct Trie 7 { 8 char c[15]; 9 Trie *nex[26]; 10 int flag; 11 Trie() 12 { 13 flag=0; 14 for(int i=0;i<26;i++) 15 nex[i]=NULL; 16 } 17 }; 18 Trie rt; 19 void inser(char *b, char *a) 20 { 21 Trie *head=&rt; 22 int len=strlen(b); 23 for(int i=0;i<len;i++) 24 { 25 int k=b[i]-'a'; 26 if(head->nex[k]==NULL) 27 { 28 Trie *node=new Trie; 29 head->nex[k]=node; 30 } 31 head=head->nex[k]; 32 } 33 head->flag=1; 34 strcpy(head->c,a); 35 } 36 37 int get(char *a) 38 { 39 Trie *head=&rt; 40 int len=strlen(a); 41 for(int i=0;i<len;i++) 42 { 43 int k=a[i]-'a'; 44 if(head->nex[k]==NULL) return 0; 45 head=head->nex[k]; 46 } 47 if(head->flag==1) 48 { 49 strcpy(b,head->c); 50 return 1; 51 } 52 else return 0; 53 } 54 int main() 55 { 56 scanf("%s",a); 57 while(scanf("%s",a)) 58 { 59 if(strcmp(a,"END")==0) break; 60 scanf("%s",b); 61 inser(b,a); 62 } 63 scanf("%s",a); 64 getchar(); // 65 while(gets(s)) //应该是读入了换行符 66 { 67 if(strcmp(s,"END")==0) break; 68 int len=strlen(s); 69 int k=0; 70 for(int i=0;i<len;i++) 71 { 72 if(islower(s[i])) 73 { 74 a[k++]=s[i]; 75 } 76 else 77 { 78 a[k]=0; 79 if(get(a)) printf("%s",b); 80 else printf("%s",a); 81 printf("%c",s[i]); 82 k=0; 83 } 84 } 85 puts(""); 86 } 87 return 0; 88 }
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <map> 5 #include <string> 6 #include <cctype> 7 using namespace std; 8 9 const int maxn=100010; 10 map< string ,string > m; 11 string s,a,b; 12 char c; 13 int main() 14 { 15 cin>>s; 16 while(cin>>a) 17 { 18 if(a=="END") break; 19 cin>>b; 20 m[b]=a; 21 } 22 cin>>s; 23 getchar(); //读入换行符,否则会读到下面的s里面去,又会输出到START行的末尾,发生难以察觉的错误-_-|| 24 a=""; 25 while(getline(cin,s)) //边读入边处理快很多,不用一次读完一行 (getline对读入换行符) 26 { 27 if(s=="END") break; 28 int len=s.length(); 29 for(int i=0;i<len;i++) 30 { 31 if(islower(s[i])) a+=s[i]; 32 else 33 { 34 if(m.count(a)) cout<<m[a]; 35 else cout<<a; 36 cout<<s[i]; 37 a=""; 38 } 39 } 40 cout<<endl; 41 } 42 return 0; 43 }
题目链接:POJ - 2001
题意:给出一些单词,判断至少要多少位才可以唯一确定一个单词,输出对应的位数。
trie树查找的时候,找到一位不同时就直接输出,找不到就输出原串即可。
1 #include <cstdio> 2 #include <cstring> 3 #include <cctype> 4 const int maxn=1010; 5 6 char s[maxn][25]; 7 struct Trie 8 { 9 Trie *nex[26]; 10 int cnt; 11 Trie () 12 { 13 cnt=0; 14 for(int i=0;i<26;i++) nex[i]=NULL; 15 } 16 }; 17 Trie rt; 18 19 void inser(char *a) 20 { 21 Trie *head=&rt; 22 int len=strlen(a); 23 for(int i=0;i<len;i++) 24 { 25 int k=a[i]-'a'; 26 if(head->nex[k]==NULL) 27 { 28 Trie *node =new Trie; 29 head->nex[k]=node; 30 } 31 head=head->nex[k]; 32 head->cnt++; 33 } 34 } 35 36 void get(char *a) 37 { 38 Trie *head=&rt; 39 int len=strlen(a); 40 for(int i=0;i<len;i++) 41 { 42 int k=a[i]-'a'; 43 if(head->nex[k]==NULL) 44 { 45 printf("%s ",a); 46 return; 47 } 48 if(head->cnt==1) 49 { 50 a[i]=0; 51 printf("%s ",a); 52 return ; 53 } 54 head=head->nex[k]; 55 } 56 printf("%s ",a); 57 return; 58 } 59 60 int main() 61 { 62 int n=0; 63 while(scanf("%s",s[n++])!=EOF) 64 { 65 inser(s[n-1]); 66 } 67 for(int i=0;i<n;i++) 68 { 69 printf("%s ",s[i]); 70 get(s[i]); 71 } 72 }
以上几题是很久之前做的了,补了一点题解,代码风格可能不太统一。
Remember the Word
题意:输入一些单词和一个长字符串,问有多少种分割该字符串的方案(只能分割成给出的单词)。
学习了lrj不用指针的trie树,感觉还不错。也是很久之前做的了,重新整理一下。
令d[i]表示从i到末尾的字符串s[i……L]可以分割的方案数
从后往前不断更新,d[i]=sum(d[i+len(x)],其中x是s[i……L]的前缀。
1 #include<cstdio> 2 #include<cstring> 3 const int maxnode=500010; 4 const int sigma=26; 5 const int N=300010; 6 const int mod=20071027; 7 int ch[maxnode][sigma]; 8 int val[maxnode]; 9 int sz; 10 inline int id(char c) {return c-'a';} 11 12 void trie_init() 13 { 14 sz=1; 15 memset(ch[0],0,sizeof(ch[0])); 16 } 17 18 void inser(char *s,int v) 19 { 20 int u=0; 21 for(int i=0;s[i];i++) 22 { 23 int c=id(s[i]); 24 if(!ch[u][c]) 25 { 26 memset(ch[sz],0,sizeof(ch[sz])); 27 val[sz]=0; 28 ch[u][c]=sz++; 29 } 30 u=ch[u][c]; 31 } 32 val[u]=v; 33 } 34 int num,dp[N],st; 35 void query(char *s) 36 { 37 int u=0; 38 for(int i=0;s[i];i++) 39 { 40 int c=id(s[i]); 41 if(ch[u][c]) 42 { 43 u=ch[u][c]; 44 if(val[u]) num=(num+dp[st+i+1])%mod; 45 } 46 else return; 47 } 48 } 49 int main() 50 { 51 char s[N],p[110]; 52 int k=0; 53 while(scanf("%s",s)!=EOF) 54 { 55 trie_init(); 56 int m; 57 scanf("%d",&m); 58 while(m--) 59 { 60 scanf("%s",p); 61 inser(p,1); 62 } 63 int len=strlen(s); 64 dp[len]=1; 65 for(int i=len-1;i>=0;i--) 66 { 67 num=0; 68 st=i; 69 query(&s[i]); 70 dp[i]=num; 71 } 72 printf("Case %d: %d ",++k,dp[0]); 73 } 74 return 0; 75 }
"strcmp()" Anyone?
题意:给n个串,问至少需要比较多少次?(每比较一个字符(如果相等' '也要比一次)算比较一次,任意两个串都要进行比较大小)
首先数据较大,如果用之前的方法建Trie树,会爆内存,因为最多可能有4000×1000×26个点,一个亿多了=_=
lrj说用左二子右兄弟表示法保存Trie树,看起来感觉很像链式前向星,只需要4000×1000个点,四百万个。总之学习了▄█▀█●
存完图还要计算比较多少次啊,看了很久才理解,太菜了。。。
void dfs(int dep,int u) { if(head[u]==-1) ans+=tot[u]*(tot[u]-1)*dep; //注释1 else { int sum=0; for(int v=head[u];~v;v=nex[v]){ sum+=tot[v]*(tot[u]-tot[v]); //注释2 } ans+=sum/2*(2*dep+1); // 注释3 for(int v=head[u];~v;v=nex[v]) dfs(dep+1,v); } }
注释1:如果这些个串要比较到' ',说明这些串完全相同,每两个串的每一位都要比较一次(按题目说的是2次)
注释2:对于中间的边(字母),每次考虑它的不同子树,同一个子树内的串先不比较。子树v中选一个串,其他子树中再选一个。求和。
注释3: 2那一步中计算重复了,所以除以2,再乘以(dep×2+1)是什么意思呢?加1就是计算当前这一层了,而dep*2是计算之前的字母,因为之前的字母相同所以每次比较次数为2.
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define CLR(m,a) memset(m,a,sizeof(m)) 5 6 const int maxnode=4000*1000+10; 7 const int sigma=26; 8 9 struct Trie 10 { 11 int head[maxnode],nex[maxnode],tot[maxnode]; 12 char ch[maxnode]; 13 int sz; 14 LL ans; //答案 15 16 void init(){sz=1; tot[0]=0;head[0]=nex[0]=-1;} 17 void inser(char *s) 18 { 19 int u=0,v,n=strlen(s); 20 tot[0]++; 21 for(int i=0;i<=n;i++){ 22 int ok=0; 23 for(v=head[u];~v;v=nex[v]) if(ch[v]==s[i]){ 24 ok=1; 25 break; 26 } 27 if(!ok){ 28 v=sz++; //新建节点 29 tot[v]=0; 30 ch[v]=s[i]; 31 nex[v]=head[u]; 32 head[u]=v; 33 head[v]=-1; 34 } 35 u=v; 36 tot[u]++; 37 } 38 } 39 void dfs(int dep,int u) 40 { 41 if(head[u]==-1) ans+=tot[u]*(tot[u]-1)*dep; 42 else { 43 int sum=0; 44 for(int v=head[u];~v;v=nex[v]){ 45 sum+=tot[v]*(tot[u]-tot[v]); //子树v中选一个串,其他子树中再选一个 46 } 47 ans+=sum/2*(2*dep+1); // 除以2是每种选法统计了两次 48 for(int v=head[u];~v;v=nex[v]) 49 dfs(dep+1,v); 50 } 51 } 52 53 LL solve() 54 { 55 ans=0; 56 dfs(0,0); 57 return ans; 58 } 59 }; 60 Trie trie; 61 const int maxn=1010; 62 int n; 63 char s[maxn]; 64 65 int main() 66 { 67 int kase=0; 68 while(scanf("%d",&n)!=EOF&&n){ 69 trie.init(); 70 for(int i=0;i<n;i++){ 71 scanf("%s",s); 72 trie.inser(s); 73 } 74 printf("Case %d: %lld ",++kase,trie.solve()); 75 } 76 return 0; 77 }
Hyper Prefix Sets
题意:找所有串的最长公公前缀,再乘以串数。
第一次写的时候,是先插入,最后查找的时候统计结果,看了别人的代码,发现可以变插入边统计。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxnode=50000*200+10; 4 const int sigma=2; 5 #define CLR(m,a) memset(m,a,sizeof(m)) 6 struct Trie{ 7 int ch[maxnode][sigma]; 8 int cnt[maxnode]; 9 int sz; 10 int ans; 11 12 void init(){ 13 CLR(ch[0],0); 14 CLR(cnt,0); 15 sz=1; 16 ans=0; 17 } 18 int idx(char c){return c-'0';} 19 void inser(char *s){ 20 int u=0,n=strlen(s); 21 for(int i=0;i<n;i++){ 22 int c=idx(s[i]); 23 if(!ch[u][c]){ 24 CLR(ch[sz],0); 25 ch[u][c]=sz++; 26 } 27 u=ch[u][c]; 28 cnt[u]++; 29 } 30 31 } 32 void find(int u,int d){ 33 for(int c=0;c<sigma;c++){ 34 if(!ch[u][c]) continue; 35 ans=max(ans,cnt[ch[u][c]]*d); 36 find(ch[u][c],d+1); 37 } 38 } 39 }; 40 Trie trie; 41 char s[210]; 42 int main() 43 { 44 int t; 45 scanf("%d",&t); 46 while(t--){ 47 int m; 48 trie.init(); 49 scanf("%d",&m); 50 while(m--){ 51 scanf("%s",s); 52 trie.inser(s); 53 } 54 trie.find(0,1); 55 printf("%d ",trie.ans); 56 } 57 }
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxnode=50000*200+10; 4 const int sigma=2; 5 #define CLR(m,a) memset(m,a,sizeof(m)) 6 struct Trie{ 7 int ch[maxnode][sigma]; 8 int cnt[maxnode]; 9 int sz; 10 int ans; 11 12 void init(){ 13 CLR(ch[0],0); 14 CLR(cnt,0); 15 sz=1; 16 ans=0; 17 } 18 int idx(char c){return c-'0';} 19 void inser(char *s){ 20 int u=0,n=strlen(s); 21 for(int i=0;i<n;i++){ 22 int c=idx(s[i]); 23 if(!ch[u][c]){ 24 CLR(ch[sz],0); 25 ch[u][c]=sz++; 26 } 27 u=ch[u][c]; 28 cnt[u]++; 29 ans=max(ans,cnt[u]*(i+1)); 30 } 31 } 32 33 }; 34 Trie trie; 35 char s[210]; 36 int main() 37 { 38 int t; 39 scanf("%d",&t); 40 while(t--){ 41 int m; 42 trie.init(); 43 scanf("%d",&m); 44 while(m--){ 45 scanf("%s",s); 46 trie.inser(s); 47 } 48 printf("%d ",trie.ans); 49 } 50 }