ybtoj字典树练习
E. 1.单词拼接
纯狗题,本题交了52发,谁看这个数据范围谁倒霉,真的我祝出题人幸福,看到5e3我果断开始map复杂度\(O(n^2logn)\)
#include <bits/stdc++.h>
using namespace std;
map<string, int> mp;
const int maxn = 5e3 + 5;
string s[maxn];
map<string, int> mp2;
signed main() {
int now = 1;
while (cin >> s[now]) {
mp[s[now]] = 1;
now++;
}
s[now] += '$';
for (int i = 1; i < now; i++) {
for (int j = 1; j <= now; j++) {
if (i == j)
continue;
string s2;
s2 += (s[i] + s[j]);
mp2[s2] = 1;
}
}
for (int i = 1; i < now; i++) {
if (mp2[s[i]] == 1) {
cout << s[i] << '\n';
}
}
return 0;
}
但是re了在我疯狂cerr后发现数据范围是\(3e4\)级别,所以这个做法T了...
正确做法:
先把所有字符串塞到trie树里,然后暴力拆分到树里匹配复杂度\(O(n)\)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 5;
int tr[maxn][200];
int tot;
char c[maxn][250];
int ed[maxn];
int getnum(char c) { return (int)(c - '0'); }
void insert(char *s) {
int p = 0;
for (int i = 0; i < strlen(s); i++) {
int k = getnum(s[i]);
if (!tr[p][k]) {
tr[p][k] = ++tot;
}
p = tr[p][k];
}
ed[p]++;
}
bool find(char *s, int l, int r) {
if (l > r)
return 0;
int p = 0;
for (int i = l; i <= r; i++) {
int k = getnum(s[i]);
if (!tr[p][k]) {
return 0;
}
p = tr[p][k];
}
return ed[p];
}
signed main() {
int now = 0;
while (cin >> c[++now]) {
insert(c[now]);
}
for (int i = 1; i <= now; i++) {
int len = strlen(c[i]);
for (int k = 0; k < len - 1; k++) {
if (find(c[i], 0, k) && find(c[i], k + 1, len - 1)) {
cout << c[i] << '\n';
break;
}
}
}
}
F. 2.最短前缀
和上题没啥本质区别还是一个思路直接把子串全塞进字典树,然后查询时尽量找到最短的且以该点结尾只有一次的子串就可以了
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int tr[maxn][200];
int tot;
char c[maxn][250];
int ed[maxn];
int getnum(char c){
return (int)(c-'0');
}
void insert(char *s,int l,int r){
int p=0;
for(int i=l;i<=r;i++){
int k=getnum(s[i]);
if(!tr[p][k]){
tr[p][k]=++tot;
}
p=tr[p][k];
}
ed[p]++;
}
int find(char *s,int l,int r){
int p=0;
for(int i=l;i<=r;i++){
int k=getnum(s[i]);
if(!tr[p][k]){
return 0;
}
p=tr[p][k];
}
return ed[p];
}
signed main(){
int now=0;
while(cin>>c[++now]){
}
for(int i=1;i<=now;i++){
for(int j=0;j<strlen(c[i]);j++){
insert(c[i],0,j);
}
}
for(int i=1;i<=now;i++){
bool flag=0;
for(int j=0;j<strlen(c[i]);j++){
if(find(c[i],0,j)==1){
cout<<c[i]<<" ";
for(int k=0;k<=j;k++){
cout<<c[i][k];
}
cout<<'\n';
flag=1;
break;
}
}
if(!flag) cout<<c[i]<<" "<<c[i]<<'\n';
}
}
G. 3.lowbit 求和
把所有数按照二进制压进去,然后再去匹配每回加反向的答案然后走同向,注意空间要开打点,我开1e7才够
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int maxn = 1e7 + 5;
int a[maxn];
int num[maxn];
int tr[maxn][3];
int tot;
const int mod = 199907210507;
void insert(int x) {
int p = 0;
for (int i = 0; i <= 61; i++) {
int now = (x >> i) & 1;
if (!tr[p][now])
tr[p][now] = ++tot;
p = tr[p][now];
num[p]++;
}
}
int find(int x) {
int p = 0;
int ans = 0;
for (int i = 0; i <= 61; i++) {
int now = (x >> i) & 1;
if (tr[p][now ^ 1])
ans = (ans + (num[tr[p][now ^ 1]] << i) % mod) % mod;
p = tr[p][now];
}
return ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
insert(a[i]);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
ans = (ans + find(a[i])) % mod;
}
cout << ans;
return 0;
}