字典树核心代码:
void init() //初始化
{
while(!qu.empty())
qu.pop();
mem(ne, 0);//
mem(fail,0);
mem(vis,0);
mem(book,0);
tot=1;
}
void Insert(char *str,int y) //插入字符串
{
int ls=strlen(str);
int rt=0;
for(int i=0; i
https://vjudge.net/contest/284538#problem/B
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束.
Output
对于每个提问,给出以该字符串为前缀的单词的数量.
Sample Input
banana
band
bee
absolute
acm
ba
b
band
abc
Sample Output
2
3
1
0
#include
#include
using namespace std;
int trie[1000010][26]; //数组形式定义字典树,值存储的是下一个字符的位置
int num[1000010]={0}; //附加值,以某一字符串为前缀的单词的数量
int pos = 1;
void Insert(char word[]) //在字典树中插入某个单词
{
int i;
int c = 0;
for(i=0;word[i];i++){
int n = word[i]-'a';
if(trie[c][n]==0) //如果对应字符还没有值
trie[c][n] = pos++;//标记节点
c = trie[c][n];
num[c]++;//以该节点数组下标字母为前缀和 的数量+1
//c = trie[c][n];num[c]++;的位置不能颠倒,自行理解
}
}
int Find(char word[]) //返回以某个字符串为前缀的单词的数量
{
int i;
int c = 0;
for(i=0;word[i];i++){
int n = word[i]-'a';
if(trie[c][n]==0)
return 0;
c = trie[c][n];
}
return num[c];
}
int main()
{
char word[11];
//memset(trie,0,sizeof(t));不能啊~否则超内存啊!!!
memset(num,0,sizeof(num))
while(gets(word)){
if(word[0]==NULL) //注意格式写法!空行gets读入的回车符会自动转换为NULL。
break;
Insert(word);
}
while(gets(word))
printf("%d\n",Find(word));
return 0;
}
#include
#include
#include
#include
using namespace std;
///用指针写的代码需要用c++提交,用G++会出现超内存的错误,总的来时,用指针的方式写比较容易理解,但是用数组的写法可能比较优
int n;
struct Node{
Node *nxt[26];
int flag;
Node(){
for(int i=0;i<26;i++)
nxt[i]=NULL;
flag=0;
}
};
Node *root;
void init()
{
root=new Node();
}
void delete_tree(Node * root)
{
//递归删除
for(int i=0; i<26; i++)
if(root->nxt[i] != NULL)
delete_tree(root->nxt[i]);
delete root;
}
void ins(char *s){
int len=strlen(s);
Node *now=root;
for(int i=0;inxt[to]==NULL)
now->nxt[to]=new Node();
now=now->nxt[to];
now->flag++;
}
}
char op[11];
int fid(char *s)
{
int len=strlen(s);
Node *now=root;
for(int i=0;inxt[to]==NULL)
return 0;
now=now->nxt[to];
}
return now->flag;
}
int main()
{
init();
while(gets(op)){
if(op[0]==NULL)
break;
ins(op);
}
while(scanf("%s",op)!=EOF)
printf("%d\n",fid(op));
delete_tree(root);
return 0;
}
参考:https://blog.csdn.net/qq_36979930/article/details/81267775
https://vjudge.net/problem/CSU-1216
给定一些数,求这些数中两个数的异或值最大的那个值
Input
多组数据。第一行为数字个数n,1 <= n <= 10 ^ 5。接下来n行每行一个32位有符号非负整数。
Output
任意两数最大异或值
Sample Input
3
3
7
9
贪心找最大异或值:
1.把每一个数以二进制形式从高位到低位插入trie树中
2.依次枚举每个数,在trie中贪心,即当前为0则向1走,为1则向0走。
异或运算有一个性质,就是对应位不一样则为1,要使结果最大化,就要让越高的位为1,所以找与一个数使得两数的异或结果最大,就需要从树的根结点(也就是最高位)开始找,如果对应位置的这个数是0,优先去找那一位为1的数,否则再找0;同理,如果对应位置的这个数是1,优先去找那一位为0的数,否则再找1。最终找到的数就是跟这个数异或结果最大的数。
对于n个数,每个数找一个这样的数并算出结果求其中的最大值即可
#include
using namespace std;
const int MaxN = 1e5 + 5;
LL val[32 * MaxN]; //点的值
int ch[32 * MaxN][2]; //边的值
int tot; //节点个数
void add(LL x) { //往 01字典树中插入 x
int u = 0;
for(int i = 32; i >= 0; i--)
{
int v = (x >> i) & 1;// x>>i 先得到x的二进制最高位 作为末位,&1再把它取出来
if(!ch[u][v])
{ //如果节点未被访问过
ch[tot][0] = ch[tot][1] = 0; //将当前节点的边值初始化
val[tot] = 0; //节点值为0,表示到此不是一个数
ch[u][v] = tot++; //边指向的节点编号
}
u = ch[u][v]; //下一节点
}
val[u] = x; //节点值为 x,即到此是一个数
}
LL query(LL x) {
int u = 0;
for(int i = 32; i >= 0; i--)
{
int v = (x >> i) & 1;// x>>i 先得到x的二进制最高位 作为末位,&1再把它取出来
//利用贪心策略,优先寻找和当前位不同的数
if(ch[u][v^1])//选择不同于ch[u][v]的那一条路
u = ch[u][v^1];
else
u = ch[u][v];
}
return val[u]; //返回结果
}
#include
#include
#define ll long long
using namespace std;
const int MAXN=1e5+10;
int a[MAXN],n,ch[MAXN][2],val[MAXN],tot=1;
int add(int x)
{
int u=0;
for(int i=32;i>=0;i--)
{
int v=(x>>i)&1;
if(!ch[u][v])
{
ch[tot][0]=ch[tot][1]=0;
val[tot]=0;
ch[u][v]=tot++;
}
u=ch[u][v];
}
val[u]=x;
}
int query(int x)
{
int u=0;
for(int i=32;i>=0;i--)
{
int v=(x>>i)&1;
if(ch[u][v^1])
u=ch[u][v^1];
else
u=ch[u][v];
}
return val[u];
}
int main()
{
cin>>n;
ch[0][1]=ch[0][0]=0;
for(int i=0;i>a[i];
add(a[i]);
}
int ans=0;
for(int i=0;i
https://vjudge.net/problem/HDU-4825
Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeus 发起M次询问,每次询问中包含一个正整数 S ,之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大。Prometheus 为了让 Zeus 看到人类的伟大,随即同意 Zeus 可以向人类求助。你能证明人类的智慧么?
Input
输入包含若干组测试数据,每组测试数据包含若干行。
输入的第一行是一个整数T(T < 10),表示共有T组数据。
每组数据的第一行输入两个正整数N,M(<1=N,M<=100000),接下来一行,包含N个正整数,代表 Zeus 的获得的集合,之后M行,每行一个正整数S,代表 Prometheus 询问的正整数。所有正整数均不超过2^32。
Output
对于每组数据,首先需要输出单独一行”Case #?:”,其中问号处应填入当前的数据组数,组数从1开始计算。
对于每个询问,输出一个正整数K,使得K与S异或值最大。
Sample Input
2
3 2
3 4 5
1
5
4 1
4 6 5 6
3
Sample Output
Case #1:
4
3
Case #2:
4
#include
#include
#include
#include
#include
#define ll long long
const int maxn=1e5+10;
using namespace std;
int t,n,m;
ll a[maxn];
ll ch[32*maxn][2],val[32*maxn];//注意数组的开的大小32*max!否则运行错误
int tot=1;
void add(ll x)
{
int u=0;
for(int i=32;i>=0;i--)
{
int v=(x>>i)&1;
if(!ch[u][v])
{
ch[tot][0]=ch[tot][1]=0;
val[tot]=0;
ch[u][v]=tot++;
}
u=ch[u][v];
}
val[u]=x;
}
ll querry(ll x)
{
int u=0;
for(int i=32;i>=0;i--)
{
int v=(x>>i)&1;
if(ch[u][v^1])
u=ch[u][v^1];
else
u=ch[u][v];
}
return val[u];
}
int main()
{
scanf("%lld",&t);
int t2=t;
while(t--)
{
ch[0][0]=ch[0][1]=0;
scanf("%lld%lld",&n,&m);
for(int i=0;i