Trie 树
Trie树
定义
\(Trie 是一棵有根树,每个点至多有 |Σ| 个后继边,每条边上有一个字符。\)
\(每个点表示一个前缀:从跟到这个点的边上的字符顺次连接形成的字符串。\)
\(每个点还有一个终止标记:是否这个点代表的字符串是一个字典串。\)
\(可以支持向 Trie 插入新字典串,删除字典串,查询某字符串是否是字典串,以及一些稍微复杂的查询。\)
\(作为一个数据结构,它理所应当的还可以进行持久化。\)
模板
普通Trie树
字典树
struct Trie{
int tot,ch[maxn][27],biao[maxn];
void insert(string s){
int u=0,lenth=s.length();
for(int i=0;i<lenth;i++){
int k=s[i]-'a';
if(!ch[u][k])ch[u][k]=++tot;
u=ch[u][k];
}
biao[u]++;
return;
}
};
01Trie树
struct Trie{
int tot,ch[maxn][2],biao[maxn];
void insert(int x){
int u=0;
for(int i=30;i>=0;i--){
int k=(x>>i)&1;
if(!ch[u][k])ch[u][k]=++tot;
u=ch[u][k];
}
biao[u]++;
return;
}
int find(int x){ //找异或最大值
int u=0,ans=0;
for(int i=30;i>=0;i--){
int k=(x>>i)&1;
if(ch[u][k^1])u=ch[u][k^1],k=1;
else u=ch[u][k],k=0;
ans+=k*(1<<i);
}
return ans;
}
}T;
可持久化Trie树
int rt[maxn];
struct Trie{
int tot;
int ch[maxn][2],cnt[maxn];
void insert(int now,int pre,int v){
for(int i=25;i>=0;i--){
int k=(v>>i)&1;
ch[now][k^1]=ch[pre][k^1];
ch[now][k]=++tot;
cnt[ch[now][k]]=cnt[ch[pre][k]]+1;
now=ch[now][k];pre=ch[pre][k];
}
return ;
}
int query(int now,int pre,int v){
int ans=0;
for(int i=25;i>=0;i--){
int k=(v>>i)&1;
if(cnt[ch[now][k^1]]-cnt[ch[pre][k^1]]){
now=ch[now][k^1];pre=ch[pre][k^1];
ans+=(1<<i);
}
else now=ch[now][k],pre=ch[pre][k];
}
return ans;
}
}T;
int main(){
//增加个新节点
rt[1]=++T.tot;
T.insert(rt[1],rt[0],0); //注意这里
}
例题
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=1e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
const double eps=1e-8;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
struct Trie{
int tot,ch[maxn][2],biao[maxn];
void insert(int x){
int u=0;
for(int i=30;i>=0;i--){
int k=(x>>i)&1;
if(!ch[u][k])ch[u][k]=++tot;
u=ch[u][k];
}
biao[u]++;
return;
}
int find(int x){
int u=0,ans=0;
for(int i=30;i>=0;i--){
int k=(x>>i)&1;
if(ch[u][k^1])u=ch[u][k^1],k=1;
else u=ch[u][k],k=0;
ans+=k*(1<<i);
}
return ans;
}
}T;
int n,a[maxn],ans;
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();T.insert(a[i]);
}
for(int i=1;i<=n;i++)ans=max(ans,T.find(a[i]));
printf("%d\n",ans);
return 0;
}
2.假的字符串
先构建出字典树,然后逐个字符串判定可行性。
考虑 \(S_i\) 在字典树上的每个节点,如果有多于一个后继边,则 \(S_i\) 使用的字母必须小于其他字母。
等价于判定 \(26\) 个点的有向图上是否有环(拓扑排序)。
额外需要注意不能有任何其他串等于 \(S_i\) 的前缀,即路径上不能有其他串的的终止节点。
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=1e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
const double eps=1e-8;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
struct Trie{
int tot,ch[maxn][27],biao[maxn];
void insert(string s){
int u=0,lenth=s.length();
for(int i=0;i<lenth;i++){
int k=s[i]-'a';
if(!ch[u][k])ch[u][k]=++tot;
u=ch[u][k];
}
biao[u]++;
return;
}
int in[27],ed[26][26];
int get_ans(string s){
memset(ed,0,sizeof(ed));
memset(in,0,sizeof(in));
int u=0,lenth=s.length();
for(int i=0;i<lenth;i++){
if(biao[u])return 0;
int k=s[i]-'a';
for(int j=0;j<26;j++){
if(j==k || !ch[u][j] || ed[k][j])continue;
ed[k][j]=1;
in[j]++;
}
u=ch[u][k];
}
queue<int>q;
for(int i=0;i<26;i++)if(!in[i])q.push(i);
while(!q.empty()){
int u=q.front();q.pop();
for(int j=0;j<26;j++){
if(ed[u][j]){
in[j]--;
if(!in[j])q.push(j);
}
}
}
for(int i=0;i<26;i++)if(in[i])return 0;
return 1;
}
}T;
int n;
string a[30001];
int main(){
n=read();
for(int i=1;i<=n;i++){cin>>a[i];T.insert(a[i]);}
vector<string>ans;
for(int i=1;i<=n;i++){
if(T.get_ans(a[i]))ans.pb(a[i]);
}
printf("%d\n",(int)ans.size());
for(auto i:ans)cout<<i<<endl;
return 0;
}
3.最大异或和
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e7+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
const double eps=1e-8;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int rt[maxn];
struct Trie{
int tot;
int ch[maxn][2],cnt[maxn];
void insert(int now,int pre,int v){
for(int i=25;i>=0;i--){
int k=(v>>i)&1;
ch[now][k^1]=ch[pre][k^1];
ch[now][k]=++tot;
cnt[ch[now][k]]=cnt[ch[pre][k]]+1;
now=ch[now][k];pre=ch[pre][k];
}
return ;
}
int query(int now,int pre,int v){
int ans=0;
for(int i=25;i>=0;i--){
int k=(v>>i)&1;
if(cnt[ch[now][k^1]]-cnt[ch[pre][k^1]]){
now=ch[now][k^1];pre=ch[pre][k^1];
ans+=(1<<i);
}
else now=ch[now][k],pre=ch[pre][k];
}
return ans;
}
}T;
int n,m,sum;
int main(){
n=read();m=read();
rt[1]=++T.tot;
T.insert(rt[1],rt[0],0); //注意这里
n++;
for(int i=2;i<=n;i++){
sum^=read();rt[i]=++T.tot;
T.insert(rt[i],rt[i-1],sum);
}
while(m--){
char ch[2];cin>>ch[0];
if(ch[0]=='A'){
int x=read();sum^=x;n++;rt[n]=++T.tot;
T.insert(rt[n],rt[n-1],sum);
}
else {
int l=read(),r=read(),x=read();
int now=sum^x;
cout<<T.query(rt[r],rt[l-1],now)<<endl;
}
}
return 0;
}