给定N个字符串S1,S2…SN,接下来进行M次询问,每次询问给定一个字符串T,求S1~SN中有多少个字符串是T的前缀。
输入字符串的总长度不超过106,仅包含小写字母。
输入格式
第一行输入两个整数N,M。
接下来N行每行输入一个字符串Si。
接下来M行每行一个字符串T用以询问。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
输入样例:
3 2
ab
bc
abc
abc
efg
输出样例:
2
0
#include
using namespace std;
const int N = 1000010, M = 500010;
int son[M][26], cnt[M], idx;
char str[N];
int n, m;
void insert()
{
int p = 0; //根节点
for(int i = 0; str[i]; i++)
{
int &s = son[p][str[i] - 'a'];
if(!s) s = ++idx; //分配虚拟内存,表示这个节点已经有了
p = s;
}
cnt[p]++; //表示以p结尾的字符串的个数
}
int query()
{
int p = 0, res = 0;
for(int i = 0; str[i]; i++)
{
int &s = son[p][str[i] - 'a'];
if(!s) break; //表示没有找到
p = s;
res += cnt[p];
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
while(n--)
{
scanf("%s", str);
insert();
}
while(m--)
{
scanf("%s", str);
printf("%d\n", query());
}
return 0;
}
#include
using namespace std;
const int N = 1000010;
char str[N];
int n, m;
class Trie
{
private:
int cnt = 0;
Trie *next[26] = {nullptr};
public:
Trie(){}
void insert()
{
Trie *root = this;
for(int i = 0; str[i]; i++)
{
if(root->next[str[i] - 'a'] == nullptr) root->next[str[i] - 'a'] = new Trie();
root = root->next[str[i] - 'a'];
}
root->cnt++;
}
int query()
{
Trie *root = this;
int res = 0;
for(int i = 0; str[i]; i++)
{
if(root->next[str[i] - 'a'] == nullptr) break;
root = root->next[str[i] - 'a'];
res += root->cnt;
}
return res;
}
};
int main()
{
Trie *trie = new Trie();
scanf("%d%d", &n, &m);
while(n--)
{
scanf("%s", str);
trie->insert();
}
while(m--)
{
scanf("%s", str);
printf("%d\n", trie->query());
}
return 0;
}
在给定的N个整数A1,A2……AN中选出两个进行xor(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数N。
第二行输入N个整数A1~AN。
输出格式
输出一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai<231
输入样例:
3
1 2 3
输出样例:
3
- 暴力法:双重循环枚举所有数对,找到最大值。
- 前缀树优化掉第二层循环。思路:每个数有31位, 从高位到低位依次比较每一位,保证相同位是不同的值,就可以保证异或值最大。
- 所以就可以用前缀树来储存所有数的31位(用前缀树的原因:每个数二进制只有两个数0和1,用前缀树可以增加查询效率)。然后选一个数从高位起,在前缀树里找相应位不同的数,如果有,则可以累加答案,如果没有就不用累加答案。
#include
#include
using namespace std;
const int N = 100010, M = 3100010;
int son[M][2], idx;
int a[N];
int n;
void insert(int x)
{
int p = 0;
for(int i = 30; i >= 0; i--)
{
int &s = son[p][x >> i & 1];
if(!s) s = ++idx;
p = s;
}
}
int query(int x)
{
int p = 0, res = 0;
for(int i = 30; i >= 0; i--)
{
int s = x >> i & 1;
if(son[p][!s]) //下一个不同存在
{
res += 1 << i; //累加这一位
p = son[p][!s];
}
else p = son[p][s];
}
return res;
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
cin >> a[i];
insert(a[i]);
}
int res = 0;
for(int i = 0; i < n; i++) res = max(res, query(a[i]));
cout << res << endl;
return 0;
}
给定一个树,树上的边都具有权值。
树中一条路径的异或长度被定义为路径上所有边的权值的异或和:
⊕ 为异或符号。
给定上述的具有n个节点的树,你能找到异或长度最大的路径吗?
输入格式
第一行包含整数n,表示树的节点数目。
接下来n-1行,每行包括三个整数u,v,w,表示节点u和节点v之间有一条边权重为w。
输出格式
输出一个整数,表示异或长度最大的路径的最大异或和。
数据范围
1≤n≤100000,
0≤u,v0≤w<231
输入样例:
4
0 1 3
1 2 4
1 3 6
输出样例:
7
样例解释
样例中最长异或值路径应为0->1->2,值为7 (=3 ⊕ 4)
#include
#include
#include
using namespace std;
const int N = 100010, M = 30000010;
int son[M][2], idx;
int h[N], e[M], w[M], ne[M], cnt;
int a[N];
int n;
void add(int a, int b, int c)
{
e[cnt] = b; w[cnt] = c; ne[cnt] = h[a]; h[a] = cnt++;
}
void insert(int x)
{
int p = 0;
for(int i = 30; i >= 0; i--)
{
int &s = son[p][x >> i & 1];
if(!s) s = ++idx;
p = s;
}
}
int query(int x)
{
int p = 0, res = 0;
for(int i = 30; i >= 0; i--)
{
int s = x >> i & 1;
if(son[p][!s])
{
res += 1 << i;
p = son[p][!s];
}
else p = son[p][s];
}
return res;
}
void dfs(int u, int fa, int sum)
{
a[u] = sum;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(j != fa) dfs(j, u, sum ^ w[i]);
}
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);
for(int i = 0; i < n - 1; i++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
dfs(0, -1, 0);
for(int i = 0; i < n; i++) insert(a[i]);
int res = 0;
for(int i = 0; i < n; i++) res = max(res, query(a[i]));
cout << res <<endl;
}