视频讲解戳这里
何为字典树:
如图所示:
每个字符有很多个分支,打黄色标记的就是字符串的结尾,所以这颗字典树中有哪些字符串呢,"ab","ay","ayf","c","cc","cd",其他的枝没有画全。
如何存储:
顺序存储字符串:“ab”“ay”“ayf”“c”“cc”“cd”……(节点编号讲究先到先得)
数组tree[i][j]:代表i节点的第j个儿子的节点编号。(获取第几个孩子可以s[i]-'a',以图中5节点举例,就是tree[0][2]=5)
数组flag[i]:为true代表到该节点为一个字符串
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
int tree[maxn][30],tot=0;
bool flag[maxn];
void add(char *s){
int root=0,id,len=strlen(s);
for(int i=0;i<len;++i){
id=s[i]-'a';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
flag[root]=true;
}
bool find(char *s){
int root=0,id,len=strlen(s);
for(int i=0;i<len;++i){
id=s[i]-'a';
if(!tree[root][id]) return false;
root=tree[root][id];
}
if(flag[root]) return true;
else return false;
}
char a[1005];
int main(){
add(a);
find(a);
return 0;
}
例题归纳:
1.HDU 1251 统计难题
题目大意:统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
解题思路:增加一个sum数组把到一个节点 (以该节点为尾的字符串作为前缀) 的字符数存储起来。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e6+5;
int tree[maxn][30],sum[maxn],tot=0;
void add(char *s){
int len=strlen(s);
int root=0;
for(int i=0;i<len;++i){
int id=s[i]-'a';
if(!tree[root][id]) tree[root][id]=++tot;
sum[tree[root][id]]++;
root=tree[root][id];
}
}
ll find(char *s){
ll res=0;
int root=0,len=strlen(s);
for(int i=0;i<len;++i){
int id=s[i]-'a';
if(!tree[root][id]) return 0;
root=tree[root][id];
}
return sum[root];
}
char tp[maxn];
int main(){
std::ios::sync_with_stdio(0);
while(gets(tp)){
if(tp[0]=='\0') break;
add(tp);
}
while(scanf("%s",tp)!=EOF){
printf("%lld\n",find(tp));
}
return 0;
}
2.HDU 2072 单词数
题目大意:就是统计一篇文章里不同单词的总数。
解题思路:在add的时候存储一下就行了。(不了解stringstream戳我)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e6+5;
int tree[maxn][30];
bool flag[maxn];
int ans=0,tot=0;
void add(string s){
int root=0,id;
for(int i=0;i<(int)s.size();++i){
id=s[i]-'a';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
if(!flag[root]) ans++;
flag[root]=1;
}
int main(){
std::ios::sync_with_stdio(0);
string s,tp;
while(getline(cin,s)){
if(s=="#") break;
stringstream ss(s);
while(ss>>tp){
add(tp);
}
cout<<ans<<endl;
ans=0;
for(int i=0;i<=tot;++i){
flag[i]=0;
for(int j=0;j<30;++j){
tree[i][j]=0;
}
}
tot=0;
}
return 0;
}
结合set也很简单;
#include<bits/stdc++.h>
using namespace std;
int main() {
string str1,str2;
while(getline(cin,str1)) {
if(str1 == "#")
break;
stringstream stream(str1);
set<string> tp;
while(stream>>str2) {
tp.insert(str2);
}
cout<<tp.size()<<endl;
}
return 0;
}
3.POJ 2001 Shortest Prefixes
题目大意:求能代表字符串的最小前缀,独一无二,没有歧义的。
解题思路:用sum[root]记录每个节点的数量,只要在find时候走到sum[root]==1的时候说明只有该字符串到过这,那么返回字符串就可以了。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e6+5;
int tree[maxn][30];
bool flag[maxn];
int tot=0,sum[maxn];
void add(char *s){
int root=0,id,len=strlen(s);
for(int i=0;i<len;++i){
id=s[i]-'a';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
sum[root]++;
}
flag[root]=1;
}
string find(char *s){
int root=0,id,i,len=strlen(s);
string ans="";
for(i=0;i<len;++i){
id=s[i]-'a';
root=tree[root][id];
ans+=s[i];
if(sum[root]==1) return ans;
}
return ans;
}
char a[1005][25];
int main(){
int cnt=0;
while(scanf("%s",a[cnt++])!=EOF){
add(a[cnt-1]);
}
for(int i=0;i<cnt;++i){
cout<<a[i]<<" "<<find(a[i])<<endl;
}
return 0;
}
4.POJ 3630 Phone List
题目大意:给你很多字符串,问你是不是任何一个字符串都不是其他字符串的前缀,输出YES或NO
解题思路:用sum[root]记录每个节点的数量,如果find一个字符串时,他走过的路上的节点sum值都大于等于2,说明他肯定是某个或者某些字符串的前缀,返回false;
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e6+5;
int tree[maxn][15],sum[maxn],tot=0;
char a[10005][15];
int T,n;
void add(char *s) {
int root=0,id,len=strlen(s);
for(int i=0; i<len; ++i) {
id=s[i]-'0';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
sum[root]++;
}
}
bool find(char *s) {
int root=0,id,len=strlen(s),tp=0;
for(int i=0; i<len; ++i) {
id=s[i]-'0';
root=tree[root][id];
if(sum[root]>=2) tp++;
}
if(tp==len) return false;
else return true;
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
for(int i=0; i<n; ++i) {
scanf("%s",a[i]);
add(a[i]);
}
bool pd=0;
for(int i=0;i<n;++i){
if(!find(a[i])){
pd=1;
break;
}
}
if(!pd) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
for(int i=0;i<=tot;++i){
for(int j=0;j<10;++j){
tree[i][j]=0;
}
}
}
return 0;
}
稍难:
5.POJ 1816 Wild Words
题目大意:给你n个模板字符串和m个待匹配字符串,模板字符串中有‘?’可以匹配任意一个字符,‘*’可以匹配0个1个或者多个字符串。问待匹配字符串可以和哪些模板字符串相匹配。题解戳我
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e6+5;
int tree[maxn][30],tot=0;
bool flag[maxn],vis[maxn];
int n,m,p[maxn];
int add(char *s) {
int root=0,id,len=strlen(s);
for(int i=0; i<len; ++i) {
if(s[i]=='*') id=26;
else if(s[i]=='?') id=27;
else id=s[i]-'a';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
flag[root]=1;
return root;
}
void find(char *s,int nroot,int pos) {
int root=nroot,id,len=strlen(s);
if(pos>len) return;
if(pos==len && flag[root]) {
vis[root]=1;
}
if(tree[root][26]) { //有*
for(int j=pos; j<=len; ++j) {
find(s,tree[root][26],j);
}
}
if(tree[root][27]) { //有?
find(s,tree[root][27],pos+1);
}
id=s[pos]-'a';
if(s[pos]>='a'&&s[pos]<='z'&&tree[root][id]) {
find(s,tree[root][id],pos+1);
}
}
char tp[50];
int main() {
scanf("%d%d",&n,&m);
for(int i=0; i<n; ++i) {
scanf("%s",tp);
p[i]=add(tp);
}
while(m--) {
scanf("%s",tp);
find(tp,0,0);
bool flag=0;
for(int i=0; i<n; ++i) {
if(vis[p[i]]) {
cout<<i<<" ";
flag=1;
}
}
if(!flag) cout<<"Not match"<<endl;
else cout<<endl;
for(int i=0; i<n; ++i) vis[p[i]]=0;
}
return 0;
}
6.HDU 1247 Hat’s Words
题目大意:给你字符串,输出其中能由其他两个字符串组合而成的字符串。题解戳我
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e6+5;
int tree1[maxn][30],tree2[maxn][30];
//一正序,一反序
bool flag1[maxn],flag2[maxn];
int vis[maxn];
int tot1=0,tot2=0;
void add1(char *s){
int root=0,id,len=strlen(s);
for(int i=0;i<len;++i){
id=s[i]-'a';
if(!tree1[root][id]) tree1[root][id]=++tot1;
root=tree1[root][id];
}
flag1[root]=true;
}
void add2(char *s){
int root=0,id,len=strlen(s);
for(int i=0;i<len;++i){
id=s[i]-'a';
if(!tree2[root][id]) tree2[root][id]=++tot2;
root=tree2[root][id];
}
flag2[root]=true;
}
void find1(char *s){
int root=0,id,len=strlen(s);
for(int i=0;i<len;++i){
id=s[i]-'a';
root=tree1[root][id];
if(flag1[root]) vis[i]++;
}
}
void find2(char *s){
int root=0,id,len=strlen(s);
for(int i=0;i<len;++i){
id=s[i]-'a';
root=tree2[root][id];
if(flag2[root]) vis[len-i-2]++;
}
}
char a[50005][100];
set<string> ans;
int main(){
int cnt=0;
while(scanf("%s",a[cnt++])!=EOF){
add1(a[cnt-1]);
strrev(a[cnt-1]);
add2(a[cnt-1]);
strrev(a[cnt-1]);
}
for(int i=0;i<cnt;++i){
int len=strlen(a[i]);
for(int j=0;j<len;++j) vis[j]=0;
find1(a[i]);
strrev(a[i]);
find2(a[i]);
strrev(a[i]);
for(int j=0;j<len;++j){
if(vis[j]>=2){
string tp=a[i];
ans.insert(tp);
break;
}
}
}
for(auto it=ans.begin();it!=ans.end();++it){
cout<<*it<<endl;
}
return 0;
}
7.POJ 2513 Colored Sticks
题目大意:给你两段有颜色的木棒,只有相同的颜色才能连接起来,问你最后给你的木棒最后能不能拼成一根长木棒。题解戳我
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e6+5;
int tree[maxn][30],sum[maxn];
int vis[maxn],num[maxn];
int tot=0,cnt=0;
int f[maxn],r[maxn];
int add(char *s) {
int root=0,id,len=strlen(s);
for(int i=0; i<len; ++i) {
id=s[i]-'a';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
if(!vis[root]) vis[root]=++cnt;
return vis[root];
}
void init() {
for(int i=0; i<maxn; ++i) {
f[i]=i;
r[i]=0;
}
}
int find(int x) {
if(x=f[x]) return f[x];
else return f[x]=find(f[x]);
}
void unite(int x,int y) {
x=find(x);
y=find(y);
if(x==y) return ;
if(r[x]<r[y]) f[x]=y;
else {
f[y]=x;
if(r[x]==r[y]) r[y]++;
}
}
char s1[20],s2[20];
int main() {
int tp1,tp2;
init();
while(scanf("%s%s",s1,s2)!=EOF) {
tp1=add(s1);
tp2=add(s2);
num[tp1]++;
num[tp2]++;
unite(tp1,tp2);
}
int flag=find(1),tt=0;
for(int i=1; i<=cnt; ++i) {
if(num[i]%2) tt++;
if(find(i)!=flag) {
cout<<"Impossible"<<endl;
return 0;
}
}
if(tt==0||tt==2) cout<<"Possible"<<endl;
else cout<<"Impossible"<<endl;
return 0;
}