Part2.3字符串算法-Trie 字典树
- A [Phone List](https://ac.nowcoder.com/acm/contest/956/A)
- B [The XOR Largest Pair](https://ac.nowcoder.com/acm/contest/956/B)
- C [Nikitosh 和异或](https://ac.nowcoder.com/acm/contest/956/C)
- D [Immediate Decodability](https://ac.nowcoder.com/acm/contest/956/D)
- E [L 语言](https://ac.nowcoder.com/acm/contest/956/E)
- [Secret Message 秘密信息](https://ac.nowcoder.com/acm/contest/956/F)
- H [The XOR-longest Path](https://ac.nowcoder.com/acm/contest/956/H)
A Phone List
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n;
char s[N];
int tr[N][10], cnt[N], idx;
void insert(char s[])
{
int p = 0;
for (int i = 0; s[i]; i ++ )
{
int u = s[i] - '0';
if (!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
// cout << p << " ";
}
cnt[p] ++;
// cout << endl;
}
bool query(char s[])
{
//如果走的中间遇到*说明有*的串为这个串的前缀
//自己走完了,都不用开辟新串,说明这个短串为所走的串的前缀
int p = 0;
// cout << s << endl;
for (int i = 0; s[i]; i ++ )
{
int u = s[i] - '0';
if (cnt[p]) return true;
if (!tr[p][u]) return false;
p = tr[p][u];
// printf("%d ", p);
}
// puts("");
return true;
}
void work()
{
memset(tr, 0, sizeof tr);
memset(cnt, 0, sizeof cnt);
idx = 0;
cin >> n;
bool flag = false;
for (int i = 0; i < n; i ++ )
{
cin >> s;
flag |= query(s);
// cout << query(s) << endl;
insert(s);
}
if (flag) puts("NO");
else puts("YES");
}
int main()
{
int T; cin >> T;
while (T -- )
{
work();
}
return 0;
}
B The XOR Largest Pair
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = N * 31;
int n;
int tr[M][2], idx;
void insert(int x)
{
int p = 0;
for (int i = 31; i >= 0; i -- )
{
int u = x >> i & 1;
if (!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
}
}
int query(int x)
{
int ans = 0, p = 0;
for (int i = 31; i >= 0; i -- )
{
int u = x >> i & 1;
if (tr[p][!u]) p = tr[p][!u], ans |= 1 << i;
else p = tr[p][u];
}
return ans;
}
int main()
{
cin >> n;
int ans = 0;
for (int i = 0; i < n; i ++ )
{
int x; scanf("%d", &x);
ans = max(ans, query(x));
insert(x);
}
cout << ans << endl;
return 0;
}
C Nikitosh 和异或
思路:第一次从左边走trie,第二次从右边都一次trie,然后将两处的值加起来,这是很常见的一种处理方式。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 400010, M = N * 31;
int n;
int a[N], s[N];
int tr[M][2], cnt[M], idx;
int l[N], r[N];//l[i]为1~i中异或和最大的值,r[i]为i~n中异或和最大的值
void insert(int x)
{
int p = 0;
for (int i = 31; i >= 0; i -- )
{
int u = x >> i & 1;
if (!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
}
}
int query(int x)
{
int ans = 0, p = 0;
for (int i = 31; i >= 0; i -- )
{
int u = x >> i & 1;
if (tr[p][!u]) ans |= 1 << i, p = tr[p][!u];
else p = tr[p][u];
}
return ans;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
s[i] = s[i - 1] ^ a[i];
}
int res = 0;
for (int i = 1; i <= n; i ++ )
{
res = max(res, query(s[i]));
// l[i] = max(res, l[i]);
l[i] = res;
// cout << res << endl;
insert(s[i]);
}
res = 0;
for (int i = n; i >= 1; i -- )
{
res = max(res, query(s[i]));
r[i] = res;
insert(s[i]);
}
int ans = 0;
for (int i = 1; i<= n; i ++ )
{
ans = max(ans, l[i] + r[i + 1]);
}
cout << ans << endl;
return 0;
}
D Immediate Decodability
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
char s[N];
int tr[N][2], cnt[N], idx;
void insert(char s[])
{
int p = 0;
for (int i = 0; s[i]; i ++ )
{
int u = s[i] - '0';
if (!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
}
cnt[p] ++;
}
bool query(char s[])
{
int p = 0;
for (int i = 0; s[i]; i ++ )
{
int u = s[i] - '0';
if (cnt[p]) return true;
if (!tr[p][u]) return false;
p = tr[p][u];
}
return true;
}
int main()
{
int ct = 0;
bool flag = false;//不存在前缀是false,存在时true
while (cin >> s)
{
if (s[0] == '9')
{
ct ++;
//输出结果
if (flag) printf("Set %d is not immediately decodable\n", ct);
else printf("Set %d is immediately decodable\n", ct);
//初始化
memset(tr, 0, sizeof tr);
memset(cnt, 0, sizeof cnt);
idx = 0;
flag = 0;
continue;
}
flag |= query(s);
insert(s);
}
}
E L 语言
还想着用DFS来搜索一下呢,不知道可行不可行。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
//长度不超过1MB = 1024KB = 1024 * 1024 B
//1024 * 1024 / 4
const int N = 10000010;
int n, m;
int tr[N][26], cnt[N], idx;
char s[N];
int len[N];
int ans;
void insert(char s[])
{
int p = 0;
for (int i = 0; s[i]; i ++ )
{
int u = s[i] - 'a';
if (!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
// cout << p << ' ';
}
// cout << endl;
cnt[p] ++;
}
/*
这题的难点就在于对query的考察了。
可以暴力枚举,枚举从每个位置开始搜,只要保证当前步是合法的,
从开始到最后都是合法的,这也就是ans = max(ans, j)
我们,从可以断开的位置开始搜索,一直走到不能再走为止。
*/
bool st[N];
int query()
{
int ans = 0;
memset(st, 0, sizeof st);
st[0] = true;
//枚举从那个字符开始
for (int i = 0; s[i]; i ++ )
{
// cout << i << endl;
if (!st[i]) continue;//如果当前字符不是最后一个字符的话,就不能搜索
int p = 0;
// cout << p << endl;
//这一个字符是终止符,要从下一个字符开始
for (int j = i + 1; s[j]; j ++ )
{
int u = s[j] - 'a';
if (!tr[p][u]) break;
p = tr[p][u];
if (cnt[p]) st[j] = true, ans = max(ans, j);
// cout << ans << endl;
}
}
return ans;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ )
{
cin >> s;
insert(s);
}
while (m -- )
{
cin >> s + 1;
cout << query() << endl;
}
return 0;
}
Secret Message 秘密信息
这题就是求有多少个信息是当前密码的前缀,加上当前密码是多少个信息的前缀,最后千万别忘记去重
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 500010;
int n, m;
int tr[N][2], cnt[N], ct[N], idx;
void insert(string s)
{
int len = s.size(), p = 0;
for (int i = 0; i < len; i ++ )
{
int u = s[i] - '0';
if (!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
ct[p] ++;
}
cnt[p] ++;
}
int query(string s)
{
int len = s.size(), p = 0, ans = 0, tmp = 0;
for (int i = 0; i < len; i ++ )
{
int u = s[i] - '0';
if (!tr[p][u]) break;
p = tr[p][u];
tmp ++;
//信息是密码的前缀
if (cnt[p]) ans += cnt[p];
}
//密码是信息的前缀
// cout << tmp << ' ' << len<< endl;
if (tmp == len)
{
ans += ct[p];
ans -= cnt[p];
}
return ans;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ )
{
int x; scanf("%d", &x);
string s;
for (int j = 0; j < x; j ++ )
{
int c; scanf("%d", &c);
s += c + '0';
}
insert(s);
}
for (int i = 0; i < m; i ++ )
{
int x; scanf("%d", &x);
string s;
for (int j = 0; j < x; j ++ )
{
int c; scanf("%d", &c);
s += c + '0';
}
printf("%d\n", query(s));
}
return 0;
}
H The XOR-longest Path
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
/*
这题之前绝对是见过,而且见过正解的
求树上最长的异或和路径,就是求找到两点,使得两点之间的路径
异或和是最大的。
根据异或的性质,设根节点为1号点,那么1~x的异或和异或上1~y的异或和
结果就是x~y的异或和。
用DFS搜索下,搜到一个点就将根节点的路径的异或和插入到trie树种
先insert后query
*/
const int N = 100010, M = 31 * N;
int n;
int head[N], ver[N * 2], edge[N * 2], Next[N * 2], tot;
bool st[N];
int tr[M][2], cnt[M], idx;
void add(int a, int b, int c)
{
ver[++ tot] = b, edge[tot] = c, Next[tot] = head[a], head[a] = tot;
}
void insert(int x)
{
// printf("insert_value: %d : ", x);
int p = 0;
for (int i = 31; i >= 0; i -- )
{
int u = x >> i & 1;
if (!tr[p][u]) tr[p][u] = ++ idx;
p = tr[p][u];
// cout << p << ' ';
}
// cout << p << endl;
cnt[p] ++;
}
int query(int x)
{
int ans = 0, p = 0;
for (int i = 31; i >= 0; i -- )
{
int u = x >> i & 1;
if (tr[p][!u]) ans |= 1 << i, p = tr[p][!u];
else p = tr[p][u];
}
// cout << ans << endl;
return ans;
}
int ans;
//u为当前节点,sum为此路径上的权值
void dfs(int u, int sum)
{
st[u] = true;
for (int i = head[u]; i; i = Next[i])
{
int y = ver[i], z = edge[i];
if (st[y]) continue;
// printf("sum: %d, z: %d, sum^z: %d\n", sum, z, sum ^ z);
insert(sum ^ z);
ans = max(ans, query(sum ^ z));
dfs(y, sum ^ z);
}
}
int main()
{
cin >> n;
for (int i = 0; i < n - 1; i ++ )
{
int x, y, z; scanf("%d%d%d", &x, &y, &z);
add(x, y, z), add(y, x, z);
}
insert(0);
dfs(1, 0);
cout << ans << endl;
return 0;
}