字典树题目集

 

字典树都是跟的这个博客学的→:https://blog.csdn.net/qq_38891827/article/details/80532462

这些题目也都是他里面的题目,就是把题目按难度排了个序 + 自己整理了下思路(代码也差不多

主要是为了记录一下 忘了的话以后可以翻翻看hhh

模板

const int maxn = 1e5 + 10;
int Next[maxn][26];
bool flag[maxn];
int tol;

void Insert(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) tree[root][id] = ++tot;
        root = Next[root][id];
    }
    flag[root] = true;
}

bool Find(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) return false;
        root = Next[root][id];
    }
    return true;
}
View Code

maxn这个要开多大得看具体题目 有的题目非2e6不可 有的1e5都会爆 所以很绝望...

第一道题:单词数HDU - 2072 

模板题咯 边插入边统计答案即可

 

#include 
using namespace std;

const int maxn = 1e5 + 10;
int Next[maxn][26], cntword[maxn], tol, ans;

void Insert(string s) {
    int len = s.size();
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
    }
    if (!cntword[root]) ans++;
    cntword[root] = 1;
}

string s;

int main() {
    while (getline(cin, s)) {
        ans = 0;
        memset(Next, 0, sizeof(Next));
        memset(cntword, 0, sizeof(cntword));
        tol = 0;
        if (s[0] == '#') return 0;
        stringstream ss(s);
        string s2;
        while (ss >> s2) {
            Insert(s2);
        } 
        cout << ans << '\n';
    }  
    return 0;
}
View Code

 

第二道题:统计难题HDU-1251

这道题就把flag变一下 表示成每一个单词里面每个字母出现的次数

对每一个字符串进行Find操作 如果到哪个位置没字母了 答案就是0 因为根据题意 后面给的字符串得作为前面插入的字符串的前缀

所以这个Find操作必须搜到这个字符串的结尾 所以中途无法继续下去就直接返回0

找到最后返回flag[root]就行了(代码中用的是cntword

 

#include 
using namespace std;

const int maxn = 1e6 + 10;
int Next[maxn][26];
int cntword[maxn];
int tol;

void Insert(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
        cntword[root]++;
    }
}

int query(char *s) {
    int root = 0;
    int len = strlen(s);
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) return 0;
        root = Next[root][id];
    }
    return cntword[root];
}

char s[maxn];

int main() {
    while (gets(s)) {
        if (s[0] == '\0') break;
        Insert(s);
    }
    while (~scanf("%s", s)) {
        printf("%d\n", query(s));
    }
}
View Code

 

第三题: Phone List Poj - 3630

题意就是给一些电话号码 不能存在某个是另一个的前缀

先插入 后check一下就好了 注意变为id = s[i] - '0'

#include 
#include 
#include 
using namespace std;

const int maxn = 5e5 + 10;
int Next[maxn][11], flag[maxn];
int tol;
bool ans;

void Insert(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - '0';
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
    }
    flag[root]++;
}

void check(char *s) {
    int len = strlen(s) - 1;
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - '0';
        root = Next[root][id];
        if (flag[root]) {
            ans = true;
            break;
        }
    }
}

char s[maxn][20];

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        ans = false;
        memset(Next, 0, sizeof(Next));
        memset(flag, 0, sizeof(flag));
        int n;
        tol = 0;
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%s", s[i]);
            Insert(s[i]); 
        }
        for (int i = 0; i < n; i++) {
            check(s[i]);
            if (ans) break;
        }
        if (ans) puts("NO");
        else puts("YES");
    }
    return 0;
}
View Code

第四题: Shortest Prefixes POJ - 2001

题意是给一堆字符串 然后找出每个字符串的最短前缀 这个最短前缀必须是只有他自己有的

先插入 记录一下每个单词每个字母出现的次数 到了flag[root]==1的时候就可以返回了 因为flag[root]==1就表示这个单词到这个位置只有一个字符串走过(也就是它本身

#include 
#include 
#include 
using namespace std;

const int maxn = 2e5 + 10;
int Next[maxn][26], tol, flag[maxn];
char s[1100][25];

void Insert(char *s) {
    int root = 0;
    int len = strlen(s);
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
        flag[root]++;
    }
}

void Find(char *s, char *ans) {
    int len = strlen(s);
    int root = 0;
    int cnt = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        ans[cnt++] = s[i];
        root = Next[root][id];
        if (flag[root] == 1) return;    
    }
}

int main() {
    int cnt = 0;
    while (~scanf("%s", s[cnt++])) {
        Insert(s[cnt-1]); 
    }
    for (int i = 0; i < cnt; i++) {
        char ans[1005] = {0};
        Find(s[i], ans);
        printf("%s %s\n", s[i], ans);
    }
    return 0;
}
View Code

第五题:Concatenation of Languages UVA - 10887

题意是给n,m个字符串 然后n个m个相接(s1接s2就行了不需要s2接s1

求有多少不同的字符串

n m最多有1500 直接暴力接一遍再插入 顺便统计答案就好了

注意这n+m个字符串有可能为空 以及拼接的问题

#include 
#include 
#include 
using namespace std;

const int maxn = 2e6 + 10;
int Next[maxn][26];
int tol, ans;
bool flag[maxn];
char s1[1510][50], s2[1510][50];

void init() {
    memset(flag, 0, sizeof(flag));
    memset(Next, 0, sizeof(Next));
    memset(s1, 0, sizeof(s1));
    memset(s2, 0, sizeof(s2));
    tol = ans = 0;
}

void Insert(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
    }
    if (!flag[root]) ans++;
    flag[root] = 1;
}


int main() {
    int T;
    int kase = 0;
    scanf("%d", &T);
    while (T--) {
        init();
        int n, m;
        scanf("%d%d", &n, &m);
        getchar();
        for (int i = 0; i < n; i++) gets(s1[i]);
        for (int i = 0; i < m; i++) gets(s2[i]);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                char s3[30] = {0};
                strcat(s3, s1[i]);
                strcat(s3, s2[j]);
                Insert(s3);
            }
        } 
        printf("Case %d: %d\n", ++kase, ans);
    }
    return 0;
}
View Code

第六题:What Are You Talking About HDU - 1075

题意就是两种语言存在一一对应关系 然后给一个火星文文本 如果里面的单词可以映射就打印映射后的单词

如果不行就输出原单词

map应该可以水过吧

字典树的做法就是把火星文插入字典树里面 并且把最后一个root给返回作为map的下标 映射的值就是对应的字符在那堆字符里面的下标

然后输入的火星文就去字典树里面找一下 看能不能找到 然后输出就好了

#include 
using namespace std;

const int maxn = 2e6 + 10;
int Next[maxn][26];
bool flag[maxn];
int tol;

int Insert(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
    }
    flag[root] = true;
    return root;
}

int Find(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) return -1;
        root = Next[root][id];
    }
    if (flag[root]) return root;
    return -1;
}

char s[maxn];
char chinese[maxn][20];
int mp[maxn];

int main() {
    scanf("%s", s);
    int cnt = 0;
    while (scanf("%s", chinese[cnt]) == 1) {
        if (chinese[cnt][0] == 'E') {
            scanf("%s", s);
            break;
        } 
        scanf("%s", s);
        mp[Insert(s)] = cnt;
        cnt++;
    }
    getchar();
    while (gets(s) != NULL) {
        if (s[0] == 'E') break;
        int len = strlen(s);
        char s2[15];
        for (int i = 0; i < len; i++) {
            if (s[i] >= 'a' && s[i] <= 'z' && i < len) {
                cnt = 0;
                while (s[i] >= 'a' && s[i] <= 'z') {
                    s2[cnt++] = s[i++];
                } 
                s2[cnt] = 0;
                int p = Find(s2);
                if (p != -1) printf("%s", chinese[mp[p]]);
                else printf("%s", s2);
                printf("%c", s[i]);
            } else {
                printf("%c", s[i]);
            } 
        }
        puts("");
    }      
    return 0;
}
View Code

第七题: DNA Prefix LightOJ - 1224

题意求 相同的前缀的长度*字符串数最大

边插入边求解 ans = max(ans, flag[root] * i)

#include 
#include 
#include 
using namespace std;

const int maxn = 2e6 + 10;
int Next[maxn][5], flag[maxn];
int tol;
int ans;

void Insert(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = 0;
        if (s[i] == 'A') id = 0;
        else if (s[i] == 'T') id = 1;
        else if (s[i] == 'G') id = 2;
        else id = 3;
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
        flag[root]++;
        ans = max(ans, (i + 1) * flag[root]);
    }
}

char s[55];

int main() {
    int T;
    int kase = 0;
    scanf("%d", &T);
    while (T--) {
        int n;
        scanf("%d", &n);
        tol = ans = 0;
        memset(Next, 0, sizeof(Next));
        memset(flag, 0, sizeof(flag));
        while (n--) {
            scanf("%s", s);
            Insert(s);  
        }
        printf("Case %d: ", ++kase);
        printf("%d\n", ans);
    }
    return 0;
}
View Code

第八题:Colored Sticks POJ - 2513

题意就是问给定的字符串能否头尾相接连成一条直线

有两个问题:一、连成一条    二 、头尾相接

第一个问题就是图的连通性 用并查集解决

第二个问题就是就像欧拉图 仅有两个度为奇数或者没有也行

#include 
#include 
#include 
#include <set>
using namespace std;

const int maxn = 2e6 + 10;
int Next[maxn][26], flag[maxn], vis[maxn], par[maxn];
int tol, lisan;
set<int> st;

int Insert(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
    }
    if (!vis[root]) vis[root] = ++lisan;
    if (par[lisan] == 0) par[lisan] = lisan;
    return vis[root];
}
int Find(int x) { return par[x] == x ? x : par[x] = Find(par[x]); }

void unit(int x, int y) {
    x = Find(x), y = Find(y);
    if (x != y) {
        par[x] = y;
    }
}

char s1[20], s2[20];

int main() {
    while(~scanf("%s%s", s1, s2)) {
        int x = Insert(s1), y = Insert(s2);
        flag[x]++;
        flag[y]++;
        unit(x, y);     
    }
    int r1 = 0, r2 = 0; 
    for (int i = 1; i <= lisan; i++) {
        if (Find(i) != Find(1)) {
            puts("Impossible");
            return 0;
        }
        if (flag[i] & 1) r1++;
    }
    if (r1 == 0 || r1 == 2)
        puts("Possible");
    else 
        puts("Impossible");
    return 0;
}
View Code

第九题:Hat’s Words HDU - 1247

题意 问有没有一个单词可由另外两个单词连接形成

建两棵字典树 一棵正序插入 一棵逆序插入

查询的时候就正序查一遍 倒叙查一遍 如果有flag就对对应位置上的标记

最后遍历一遍vis数组就知道有没有了

#include 
#include 
#include 
#include <string>
#include 
#include <set>
using namespace std;

const int maxn = 2e6 + 10;
int Next1[maxn][26], Next2[maxn][26], vis[55];
bool flag1[maxn], flag2[maxn];
int tol1, tol2;
set<string> ans;

void Reverse(char s[], int len) {
    int i = 0, j = len - 1;
    while (i <= j) {
        swap(s[i], s[j]);
        ++i, --j;
    }
}

void Insert1(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next1[root][id]) Next1[root][id] = ++tol1;
        root = Next1[root][id];
    }
    flag1[root] = true;
}

void Insert2(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if (!Next2[root][id]) Next2[root][id] = ++tol2;
        root = Next2[root][id];
    }
    flag2[root] = true;
}

void Find1(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len - 1; i++) {
        int id = s[i] - 'a';
        root = Next1[root][id];
        if (flag1[root]) vis[i]++;
    }
} 

void Find2(char *s) {
    int len = strlen(s);
    int root = 0;
    for (int i = 0; i < len - 1; i++) {
        int id = s[i] - 'a';
        root = Next2[root][id];
        if (flag2[root]) vis[len-2-i]++;
    }
}

char s[(int)5e4 + 10][55];

int main() {
    int cnt = 0;
    while (scanf("%s", s[cnt++]) != EOF) {
        Insert1(s[cnt-1]);
        int len = strlen(s[cnt-1]);
        Reverse(s[cnt-1], len);
//        strrev(s[cnt-1]);
        Insert2(s[cnt-1]);
        Reverse(s[cnt-1], len);
//        strrev(s[cnt-1]);
    }
    for (int i = 0; i < cnt; i++) {
        memset(vis, 0, sizeof(vis));
        int len = strlen(s[i]);
        Find1(s[i]);
//        strrev(s[i]);
        Reverse(s[i], len);
        Find2(s[i]);
//        strrev(s[i]);
        Reverse(s[i], len);
        for (int j = 0; j < len; j++) {
            if (vis[j] == 2) {
                string temp = s[i];
                ans.insert(temp);
                break; 
            }
        }
    }
    set<string>::iterator it;
    for (it = ans.begin(); it != ans.end(); it++) {
        printf("%s\n", (*it).c_str());
    }
    return 0;
}
View Code

第十题:J - Anagram Groups POJ - 2408

题意就是把所有单词都按字典序重新排一遍 把这些单词组(一个组就是他们重排完事一个单词)从大到小排序(大小指这个组有多少个字符串)输出

一样大小就按字典序输出

首先是答案怎么存储 因为得把原来的字符串按字典序输出 而且一样的只输出一遍 就是一个set 再用一个cnt记数 就一个struct就好了

然后就很简单了 每次插入的时候先把这个字符串排下序 然后插入 最后看看存没存在 给对应的结构体数组去插入原字符串以及计数器+1就好了

#include 
#include 
#include 
#include <string>
#include <set>
#include 
using namespace std;

const int maxn = 1e6 + 10;
struct ANS {
    int cnt;
    set<string> st;
} ans[maxn];
int Next[maxn][26], tol, vis[maxn];
int cnt;
bool cmp(const ANS& a, const ANS& b) {
    if (a.cnt == b.cnt) return *(a.st.begin()) < *(b.st.begin());
    return a.cnt > b.cnt;
}

void Insert(char *s) {
    int len = strlen(s);
    string ss = s;
    sort(ss.begin(), ss.end());
    int root = 0;
    for (int i = 0; i < len; i++) {
        int id = ss[i] - 'a';
        if (!Next[root][id]) Next[root][id] = ++tol;
        root = Next[root][id];
    }
    if (!vis[root]) vis[root] = ++cnt;
    ans[vis[root]].cnt++;
    string temp = s;
    ans[vis[root]].st.insert(temp);
}
char s[maxn];

int main() {
    while (~scanf("%s", s)) {
        Insert(s);
    }
    sort(ans + 1, ans + 1 + cnt, cmp);
    for (int i = 1; i <= 5; i++) {
        printf("Group of size %d: ", ans[i].cnt);
        set<string>::iterator it;
        for (it = ans[i].st.begin(); it != ans[i].st.end(); it++) {
            printf("%s ", (*it).c_str());
        }
        puts(".");
    }
    return 0;
}
View Code

还有三道比较难的题 过后再写...留坑...

 

转载于:https://www.cnblogs.com/Mrzdtz220/p/10699495.html

你可能感兴趣的:(字典树题目集)