今天学习了字典树,现在发现经过慢慢的学习,理解算法的能力逐渐的加强,现在已经比之前理解算法更快了。
首先,字典树的作用就是提供一个类似前缀的东西,然后更利于之后查找某一个数
字典数的应用可以分为一下几个方面:统计和排序大量的字符串
【注意有比较多的公共前缀的这些字符串】,求最大异或对
求最大异或和
文本词频统计
前缀匹配
字符串检索
如何建树
const int N = 2e5+5;
int idx,cnt[N];
int son[N][26];
char str[N];
void insert(char str[])
{
int p=0;
for(int i=0;str[i];i++)
{ int u=str[i]-'a';
if(!son[p][u])
son[p][u]=++idx;
p=son[p][u];
}
cnt[p]++;//cnt记录以这种情况结尾的字符串有多少个
}
查询
查询就是如果存在与这种字符不同的,则进入不同的方向。否则进入相同的方向
int query(char str[])
{
int p=0;
for(int i=0;str[i];i++)
{
int u=str[i]-'a';
if(son[p][!u]){
p=son[p][!u];
}
else{
p=son[p][u];
}
}
}
需要注意的是,开数组的时候数组的大小应该为最大长度乘以每个a[i]的所有可能情况
例题1
没什么好说的,板子题
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=1e5+10;
int a[N];
int son[N*31][2];
int idx=0;
void insert(int x){
int p=0;
for(int i=30;~i;i--){
int s=x>>i&1;
if(!son[p][s]){
son[p][s]=++idx;
}
p=son[p][s];
}
}
int query(int x){
int ans=0,p=0;
for(int i=30;~i;i--){
int s=x>>i&1;
if(son[p][!s]){
ans+=1<<i;
p=son[p][!s];
}
else{
p=son[p][s];
}
}
return ans;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
insert(a[i]);
}
int ans=0;
for(int i=0;i<n;i++){
int t=query(a[i]);
ans=max(t,ans);
}
cout<<ans;
}
例题2
判断是否存在前缀的两个方向:
(1)该字符串是否可能是其他字符串的前缀
(2)其他字符串受否可能是该字符串的前缀
对于(1)只需要判断insert该字符串时有没有建立新节点情况,若没有,则证明该字符串为某字符串的前缀
对于(2)只需要判断该字符串的任意一个点是否可能是其他字符串的结尾
代码如下
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<queue>
#include<stack>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=10000+10;
char a[10];
int f[N*10][10];
int idx;
int flag=0;
int v[N*10];
void insert(char s[]){
int judge1=0;//记录是否开新节点
int p=0;
int judge2=0;//判断是否可存在前缀
for(int i=0;s[i];i++){
int u=s[i]-'0';
if(!f[p][u]){
f[p][u]=++idx;
judge1=1;
}
p=f[p][u];
if(v[p]==1){
judge2=1;
}
}
v[p]=1;
if(judge1==0||judge2==1){//两种情况满足任意一种情况则说明不兼容
flag=1;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n;
flag=0;
idx=0;
memset(f,0,sizeof f);
memset(v,0,sizeof v);
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%s",a);
insert(a);
}
if(flag==1){
cout<<"NO\n";
}
else{
cout<<"YES\n";
}
}
}