Trie专题练习记录 1

HDU 1251 统计难题

Solution:

\(Trie\)。插入字符串时,将前缀链上的所有结点的点权加一,代表该前缀出现次数。查询时直接返回出现次数即可。注意输入格式。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=11;
const int M=5e5+10;
char s[N];
int tr[M][26];
int endpos[M];
struct trie
{
	int tot;//点数
	int rt;//根
	void init(){
		tot=0;
		rt=0;
	}
	void insert1(char s[],int lens){
		int pos=rt;
		for(int i=0;i<lens;++i){
			int v=s[i]-'a';
			if(!tr[pos][v])tr[pos][v]=++tot;
			pos=tr[pos][v];
			endpos[pos]++;
		}
		
	}
	int query(char s[],int lens){
		int pos=rt;
		for(int i=0;i<lens;++i){
			int v=s[i]-'a';
			if(!tr[pos][v])return 0;
			pos=tr[pos][v];
		}
		return endpos[pos];
	}
};
int main(){
	trie T;
	T.init();
	while(cin.getline(s,N)){
		int len=strlen(s);
		if(len==0)break;
		T.insert1(s,len);
	}
	while(cin.getline(s,N)){
		int len=strlen(s);
		printf("%d\n", T.query(s,len));
	}
	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

HDU 4825 Xor Sum

Solution:

\(Trie\)经典题。将所有数二进制拆分后插入字典树中,每次询问\(x\),对于\(x\)二进制数的每一位,尽量往相反方向走,最后统计答案即可。

Code:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
const int N=3e6+10;
int tr[N][2];
typedef long long ll;
struct trie{
	int tot;
	int rt;
	void insert(ll val){
		int pos=rt;
		for(ll i=33;i>=0;--i){
			int v = (1&(val>>(i*1ll)));
			if(!tr[pos][v])tr[pos][v]=++tot;
			pos = tr[pos][v];
		}
	}
	ll query(ll val){
		int pos=rt;
		ll ans=0;
		for(ll i=33;i>=0;--i){
			int v = (1&(val>>(i*1ll)));
			if(!tr[pos][v^1]){
				pos=tr[pos][v];
				ans=ans+(1ll*(1<<i))*(v);
			}
			else if(tr[pos][v^1]){
				pos=tr[pos][v^1];
				ans=ans+(1ll*(1<<i))*(v^1);
			}
		}
		return ans;
	}
	void init(){
		tot=rt=0;
	}
};
int n,m;
ll q[100005];
int main(){
	int cas=0;
	int t;
	trie T;

	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=0;i<=T.tot;++i){
			for(int j=0;j<=1;++j)
				tr[i][j]=0;
		}
		T.init();

		for(int i=1;i<=n;++i){
			scanf("%lld",&q[i]);
			T.insert(q[i]);
		}
		printf("Case #%d:\n",++cas);
		for(int i=1;i<=m;++i){
			ll x;
			scanf("%lld",&x);
			printf("%lld\n",T.query(x));
		}

	}
	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

HDU 5536 Chip Factory

Solution:

\(Trie\)。先将所有数拆分成二进制插入到\(Trie\)中,统计每一位出现的次数。\(n^2\)暴力枚举\(s_i+s_j\),对于每一组\(s_i+s_j\),先在\(Trie\)中将\(s_i\)\(s_j\)分别删去,即将\(s_i\)\(s_j\)的每一位二进制对应的出现次数减一,再做一遍经典的异或最大值查询,然后要记得把\(s_i\)\(s_j\)加回去,还原\(Trie\),最后对所有结果求\(max\)即可。

Code:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
const int N=3e6+10;
int tr[N][2];
typedef long long ll;
int cnt[N];
struct trie{
	int tot;
	int rt;
	void insert(ll val){
		int pos=rt;
		for(ll i=33;i>=0;--i){
			int v = (1&(val>>(i*1ll)));
			if(!tr[pos][v])tr[pos][v]=++tot;
			pos = tr[pos][v];
			cnt[pos]++;
		}
	}
	ll query(ll val,ll x,ll y){
		int pos=rt;
		ll ans=0;
		for(ll i=33;i>=0;--i){
			int v = (1&(x>>(i*1ll)));
			pos=tr[pos][v];
			cnt[pos]--;
		}
		pos=rt;
		for(ll i=33;i>=0;--i){
			int v = (1&(y>>(i*1ll)));
			pos=tr[pos][v];
			cnt[pos]--;
		}
		pos=rt;
		for(ll i=33;i>=0;--i){
			int v = (1&(val>>(i*1ll)));
			if(cnt[tr[pos][v]]==0&&cnt[tr[pos][v^1]]==0)break;
			else if(cnt[tr[pos][v]]!=0&&cnt[tr[pos][v^1]]==0){
				pos=tr[pos][v];
				ans=ans+(1ll*(1<<i))*(v);
			}
			else if(cnt[tr[pos][v]]==0&&cnt[tr[pos][v^1]]!=0){
				pos=tr[pos][v^1];
				ans=ans+(1ll*(1<<i))*(v^1);
			}
			else{
				pos=tr[pos][v^1];
				ans=ans+(1ll*(1<<i))*(v^1);
			}
		}
		pos=rt;
		for(ll i=33;i>=0;--i){
			int v = (1&(x>>(i*1ll)));
			pos=tr[pos][v];
			cnt[pos]++;
		}
		pos=rt;
		for(ll i=33;i>=0;--i){
			int v = (1&(y>>(i*1ll)));
			pos=tr[pos][v];
			cnt[pos]++;
		}
		return ans;
	}
	void init(){
		tot=rt=0;
	}
};
int n,m;
ll q[100005];
int main(){
	int cas=0;
	int t;
	trie T;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=0;i<=T.tot;++i){
			for(int j=0;j<=1;++j)
				tr[i][j]=0;
			cnt[i]=0;
		}
		T.init();

		for(int i=1;i<=n;++i){
			scanf("%lld",&q[i]);
			T.insert(q[i]);
			
		}
		ll mx=0;
		for(int i=1;i<=n;++i){
			for(int j=i+1;j<=n;++j){
				if(i==j)continue;
				ll val=q[i]+q[j];
				mx=max(mx,T.query(val,q[i],q[j])^val);
			}
		}
		printf("%lld\n",mx);

	}
	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

POJ 2001 Shortest Prefixes

Solution:

\(Trie\)。将所有字符串插入,维护\(cnt\)记录每个前缀出现的次数。依次对每个串进行查询,第一次遇到出现次数为\(1\)的前缀即为该字符串的答案。

Code:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
const int N=5e5+10;
int tr[N][26];
bool endpos[N];
int cnt[N];
struct trie{
	int tot;
	int rt;
	void insert(char s[],int lens){
		int pos=rt;
		for(int i=0;i<lens;++i){
			int v = s[i] - 'a';
			if(!tr[pos][v])tr[pos][v]=++tot;
			pos = tr[pos][v];
			cnt[pos]++;
		}
		endpos[pos] = 1;
	}
	int query(char s[],int lens){
		int pos=rt;
		int ans=0;
		for(int i=0;i<lens;++i){
			int v = s[i] - 'a';
			if(!tr[pos][v])return false;
			pos=tr[pos][v];
			if(cnt[pos]==1)return i+1;
		}
		return lens;
	}
	void init(){
		tot=rt=0;
	}
};

char sp[1001][21];
int main(){
	trie T;
	T.init();
	int con=0;
	while(scanf("%s",sp[++con])!=EOF){
		T.insert(sp[con],strlen(sp[con]));
	}

	for(int i=1;i<=con;++i){
		int vlen=T.query(sp[i],strlen(sp[i]));
		printf("%s ",sp[i]);
		for(int j=0;j<vlen;++j)printf("%c",sp[i][j]);
		puts("");
	}
}
posted @ 2021-07-13 13:19  Qquun  阅读(34)  评论(0)    收藏  举报