目录:
OP
感谢学长的讲解与付出;
感谢ph和zsl两位大佬的指导与讨论;
目前来看,字典树主要用于线性复杂判断给定字符串与字典有没有前缀关系,和给定目标下异或值的求取;
A L语言
题目大意
给定字典,问题给字符串是否为字典中的若干单词拼接起来的;
思路
个人选择了一个比较暴力的做法(只看懂了这个);
即对每一个作为单词结尾的点的后面都进行单词判断,以此更新最大长度;
详见上链吧;
代码
代码来自上链
#include <bits/stdc++.h>
using namespace std;
map<string, bool> m1;
map<string, int> m2;
char s[2000005];
int cnt = 0;
int trie[1005][28];
bool f[2000010];
bool flag[10005];
inline int read()
{
int ans = 0, fl = 1;
char i = getchar();
while (i < '0' || i > '9')
{
if (i == '-')
{
fl = -1;
}
i = getchar();
}
while (i >= '0' && i <= '9')
{
ans = ans * 10 + i - '0';
i = getchar();
}
return ans * fl;
}
inline void insert()
{
scanf("%s", s + 1);
int len = strlen(s + 1);
int pos = 0;
for (int i = 1; i <= len; i++)
{
int c = s[i] - 'a';
if (!trie[pos][c])
{
trie[pos][c] = ++cnt;
}
pos = trie[pos][c];
}
flag[pos] = 1;
}
inline int query() //查询
{
scanf("%s", s + 1);
if (m1[s + 1])
{
return m2[s + 1];
}
else
{
int len = strlen(s + 1);
int pos = 0;
int ans;
memset(f, false, sizeof(f));
f[0] = true;
for (int i = 0; i <= len; i++)
{
if (!f[i])
continue;
else
ans = i;
for (int j = i + 1, p = 0; j <= len; j++)
{
int c = s[j] - 'a';
p = trie[p][c];
if (!p)
break;
if (flag[p])
f[j] = true;
}
}
m1[s + 1] = true;
m2[s + 1] = ans;
return ans;
}
}
int main()
{
int n, m;
n = read();
m = read();
while (n--)
insert();
while (m--)
cout << query() << endl;
return 0;
}
B Secret Message 秘密信息
题目大意
用以测试的串是多少个给定串的前缀;
思路
字典树板,用给定串构造好字典树,用测试串去匹配即可;
注意一些特殊情况,如测试串是给定串的前缀等,做好标记;
代码
#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef int itn;
typedef unsigned long long ull;
inline ll read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return (ll)x*f;
}
int b[100005],g;
int ttre[500005][2],meet[500005],ed[500005],c;
int main()
{
int n,m;
cin>>n>>m;
c=1;
while(n--)
{
int k;
scanf("%d",&k);
int p=0;
for(int i=1;i<=k;i++)
{
scanf("%d",&g);
if(!ttre[p][g])ttre[p][g]=c++;//printf("*");
p=ttre[p][g];
meet[p]++;
}
meet[p]--;
ed[p]++;
}
while(m--)
{
int ans=0;
c=1;
int k;
scanf("%d",&k);
int p=0;
for(int i=1;i<=k;i++)
{
scanf("%d",&b[i]);
}
for(int i=1;i<=k;i++)
{
p=ttre[p][b[i]];
if(!p)
{
//ans+=meet[p];
break;
}
else
{
ans+=ed[p];
}
}
ans+=meet[p];
printf("%d\n",ans);
}
return 0;
}
C The XOR-longest Path
题目大意
给定树的结构和每条边的边权,求任意两点间路径上的最小边权异或值;
思路
树的性质有,任意两点间均有且只有一条路径;
异或的性质有, C = A x o r B , C x o r B = A C=AxorB,CxorB=A C=AxorB,CxorB=A;
所以,我们可以通过dfs处理出所有点与一号节点(根)之间路径的边权异或,这样无论所求两点是否为同一枝杈上,都可以 O ( 1 ) O(1) O(1) 复杂度内求出两点间的路径边权异或值;
接下来把这些处理出的异或值存入字典树,依次在字典树中匹配与其异或值最大的值,并更新答案;此部分复杂度 O ( n l o g n ) O(nlogn) O(nlogn) ;
代码
#include <stdio.h>
#include <iostream>
#include <stack>
#include <vector>
#include <algorithm>
#include <string.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef int itn;
typedef unsigned long long ull;
inline ll read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return (ll)x * f;
}
//stack<int> stk;
int ttre[3200006][2], ed[3200006], val[100005], rch[100005]; //val[300005];
vector<pair<int, int> > rod[100005];
void dfs(int x)
{
for (int i = 0; i < rod[x].size(); i++)
{
if (!rch[rod[x][i].first])
{
rch[rod[x][i].first] = 1;
val[rod[x][i].first] = (val[x] ^ rod[x][i].second);
dfs(rod[x][i].first);
}
}
}
int main()
{
int n, g, a, b, c;
while (cin >> n)
{
for (int j = 1; j <= n - 1; j++)
{
scanf("%d%d%d", &a, &b, &c);
rod[a].push_back(make_pair(b, c));
rod[b].push_back(make_pair(a, c));
} //printf("*");
rch[1] = 1;
dfs(1);
c = 1;
//printf("*");
for (int j = 1; j <= n; j++)
{
int p = 0;
for (int i = 30; i >= 0; i--)
{
g = (val[j] >> i) & 1;
if (!ttre[p][g])
ttre[p][g] = c++;
p = ttre[p][g];
}
//ed[p]++;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
int tmp = 0, p = 0;
for (int j = 30; j >= 0; j--)
{
if (ttre[p][!((val[i] >> j) & 1)])
{
tmp += 1 << j;
p = ttre[p][!((val[i] >> j) & 1)];
}
else
p = ttre[p][((val[i] >> j) & 1)];
}
ans = max(ans, tmp);
}
cout << ans << endl;
for (int i = 1; i <= n; i++)
{
val[i] = 0;
rch[i] = 0;
rod[i].clear();
}
for (int i = 0; i < c; i++)
{
ttre[i][0] = 0;
ttre[i][1] = 0;
}
}
return 0;
}
D The XOR Largest Pair
题目大意
给定一些数,求任意两数间的异或最大值;
思路
字典树板,将这些数存入字典树,依次在字典树中匹配与其异或值最大的值,并更新答案;
代码
#include <stdio.h>
#include <iostream>
#include <stack>
#include <algorithm>
#include <string.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef int itn;
typedef unsigned long long ull;
inline ll read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return (ll)x * f;
}
stack<int>stk;
int ttre[3100006][2],ed[3100006],k[100005];//val[300005];
int c;
int ans=0;
int main()
{
int n,g;
cin >> n;
c=1;
for(int j=1;j<=n;j++)
{
scanf("%d", &k[j]);
int p = 0;
for (int i = 30; i>=0; i--)
{
g = (k[j]>>i)&1;
//printf("%d*",g);
if (!ttre[p][g])
ttre[p][g] = c++;
p = ttre[p][g];
//val[p]=g;
}
ed[p]++;
}
int ans=0;
for(int i=1;i<=n;i++)
{
int tmp=0,p=0;
for(int j=30;j>=0;j--)
{
if(ttre[p][!((k[i]>>j)&1)])
{
tmp+=1<<j;
p=ttre[p][!((k[i]>>j)&1)];
}
else p=ttre[p][((k[i]>>j)&1)];
}
ans=max(ans,tmp);
}
cout<<ans;
return 0;
}
E Phone List
题目大意
给定 n 个长度不超过 10 且各异的数字串,问其中是否存在两个数字串 S,T,使得 S 是 T 的前缀。
思路
字典树板,接收到的都存入字典树后,检测字典树中是否有前缀关系即可;
注意不能用 lld 接收,存在前导零;
代码
#include <stdio.h>
#include <iostream>
#include <stack>
#include <algorithm>
#include <string.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef int itn;
typedef unsigned long long ull;
inline ll read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return (ll)x * f;
}
int g;
int ttre[100005][10], meet[100005], ed[100005], c;
char k[12];
int main()
{
int n;
itn t;
cin >> t;
while (t--)
{
int f = 0;
cin >> n;
c = 1;
while (n--)
{
scanf("%s",&k);
int p = 0;
for (int i = 0; k[i]; i++)
{
g = k[i] - '0';
//printf("%d*",g);
if (!ttre[p][g])
ttre[p][g] = c++;
p = ttre[p][g];
meet[p]++;
}
//meet[p]--;
ed[p]++;
}
for (int i = 0; i < c; i++)
{
if (ed[i] && meet[i] >= 2)
f = 1;
ed[i]=0;
meet[i]=0;
memset(ttre[i],0,sizeof ttre[i]);
}
if (f)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}
F Xor sum
题目大意
给定n个数和一个数k,求出最短的连续的一段数,使得它们的异或和大于等于k,如果没有则输出-1。
思路
对于给定的每一个数,在存入字典树之前先判断并更新答案,这样搜索范围内的只有它前面的数,此时即为固定右端点找左端点;
每个数在树中存储的结尾要记录这个数最后一次出现的下标,这样就可以使区间尽可能小;
注意无论这位的异或值是1还是0,只要合法,就应计算,之后取最大值;
代码
代码来自上链
#include <stdio.h>
#include <iostream>
#include <stack>
#include <vector>
#include <algorithm>
#include <string.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef int itn;
typedef unsigned long long ull;
inline ll read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return (ll)x * f;
}
const int N = 1e5 + 10;
int trie[N * 31][2], mx[N * 31], fa[N * 31], tot = 0;
void init()
{
trie[0][0] = trie[0][1] = 0;
for (int i = 0; i <= tot; i++)
{
mx[i] = fa[i] = 0;
}
tot = 0;
}
void insert(int x, int pos)
{
int temp[31];
for (int i = 30; i >= 1; i--)
{
temp[i] = x & 1;
x >>= 1;
}
int now = 0;
for (int i = 1; i <= 30; i++)
{
int p = now;
mx[now] = pos;
if (trie[now][temp[i]])
now = trie[now][temp[i]];
else
{
trie[now][temp[i]] = ++tot;
trie[tot][0] = trie[tot][1] = 0;
now = tot;
}
fa[now] = p;
}
mx[now] = pos;
}
int query(int r, int k, int now, int sum, int bit)
{
if (sum >= k)
return mx[now];
if (sum + (1 << (bit + 1)) <= k)
return 0;
int ans = 0;
if (trie[now][0])
{
int temp = (r & (1 << bit));
ans = max(ans, query(r, k, trie[now][0], sum + temp, bit - 1));
}
if (trie[now][1])
{
int temp = (r & (1 << bit) ^ (1 << bit));
ans = max(ans, query(r, k, trie[now][1], sum + temp, bit - 1));
}
return ans;
}
int main()
{
int T = 1;
T = read();
while (T--)
{
init();
int n, k;
n = read();
k = read();
int ans = n + 1, l = -1, r = -1;
int sum = 0;
for (int i = 1; i <= n; i++)
{
int a = read();
sum ^= a;
if (sum >= k)
{
if (ans > i)
{
ans = i;
l = 1;
r = i;
}
}
int d = query(sum, k, 0, 0, 29);
if (d > 0 && ans > i - d)
{
ans = i - d;
l = d + 1;
r = i;
}
insert(sum, i);
}
if (ans == n + 1)
printf("-1\n");
else
printf("%d %d\n", l, r);
}
}
ED
\