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;
}

posted @ 2023-09-14 15:20  jt0007  阅读(15)  评论(0编辑  收藏  举报