1 条题解

  • 1
    @ 2024-8-28 10:10:47

    做法有很多种,这里介绍一个算法,TrieTrie 树(字典树)。

    TrieTrie 树是一个很方便的存储字符串的数据结构。

    首先,我们定义

    vector ch(n, vector<char>(26));//记录字符串的二维数组,第二维度是26,因为有26个字母,我们将a-z映射到0-25
    vector cnt(n);
    int idx = 0;
    

    chch 数组通过树形结构负责记录字符串

    idxidx 则是代表树的节点编号

    cntcnt 数组在每个字符串末尾的序号处加 11 代表存在这个字符串。

    例如,a,b,c,ad,acd\text{a,b,c,ad,acd} 建立的字典树如图所示:

    这里 ch[0][0]=1ch[0][0] = 1 代表从节点 00 到节点 11 的这条边存在一条边存储着 a\text{a} 这个字符,ch[0][1]ch[0][1] 存储的则是 b\text{b} 这个字符,这里的 chch 第二维度中的 0,10,1 是通过让字符减去 a'a' 这个偏移量映射的 a,b\text{a,b}

    cnt[1]=1cnt[1] = 1 代表存在一个以这条边上的字符 a'a' 结尾的字符串,其他 cnt[]=1cnt[] = 1 处同理。

    接下来介绍 TrieTrie 树的代码:

    void insert(string s) {
       int u = 0;
            for(int i = 0; i < s.size(); i ++) {
                int v = s[i] - 'a';
                if(!ch[u][v]) ch[u][v] = ++idx;//如果没有这个点,我们就创建这个点,并且记录其编号为0,1...n-1,n
                u = ch[u][v];//如果存在这个点,我们就将u修改为边的起点
            }
            cnt[u] ++;//记录以u这条边上的字符为结尾的字符串
    }
    
     ll query(string s) {
            int u = 0;
            for(int i = 0; i < s.size(); i ++) {
                int v = s[i] - 'a';
                if(!ch[u][v]) return 0;//如果不存在这个点,返回0
                u = ch[u][v];//如果存在这个点
            }
            return cnt[u];//存在这个点就返回,看是否有以u为终点的字符串
        }
    
    

    分析 TireTire 树的时间复杂度:

    最多是所有节点都不重复,那么就是每个字符串的字符都对应一个点,总节点数等于所有字符串长度的总和,一共有 2626 个字符,时间复杂度即为 O(26n)O(26n) 这里的 nn 代表所有字符串的长度。

    拓展,TrieTrie 树能做的还有很多,可以映射 az,AZ,09a-z,A-Z,0-9 ,只要修改映射值,就能存储其他东西。

    这里贴一个 TrieTrie 树的板子

    struct Trie {
        std::map<char, int> mp;
        int idx = 0;
        std::vector<std::vector<int>> ch;
        std::vector<int> cnt;
    
        Trie(int n) {
            ch.resize(n, std::vector<int>(63));
            cnt.resize(n);
            idx = 0;
            ll id = 0;
            for(char c = 'a'; c <= 'z'; c ++) mp[c] = ++id;
            for(char c = 'A'; c <= 'Z'; c ++) mp[c] = ++id;
            for(char c = '0'; c <= '9'; c ++) mp[c] = ++id;
        }
    
        void insert(std::string s) {
            int u = 0;
            for(int i = 0; i < s.size(); i ++) {
                int v = mp[s[i]];
                if(!ch[u][v]) ch[u][v] = ++idx;
                u = ch[u][v];
            }
            cnt[u] ++;
        }
    
        ll query(std::string s) {
            int u = 0;
            for(int i = 0; i < s.size(); i ++) {
                int v = mp[s[i]];
                if(!ch[u][v]) return 0;
                u = ch[u][v];
            }
            return cnt[u];
        }
        //清空函数
        void clear() {
            for(int i = 0; i <= idx; i ++) {
                cnt[i] = 0;
                for(int j = 0; j <= 62; i ++) {
                    ch[i][j] = 0;
                }
            }
            idx = 0;
        }
    };
    
    //Trie的遍历
    function<void(int, int)> dfs = [&](int p, int ans) {
            ans += cnt[p];
            bool f = 1;
            for(int i = 0; i < 26; i ++) {
                if(ch[p][i]) {
                    dfs(ch[p][i], ans), f = false;
                }
            }
            if(f) {
                anss = max(anss, ans);
            }
        };
    
    
    //调用板子
    Trie t(N);
    

    对于这道题目,只需要用 TrieTrie 树记录名单,再开一个 mapmap 数组映射,每次询问的时候,在 mapmap 中记录该询问的字符串,如果询问的字符串在 TrieTrie 树上存在,且未在 mapmap 中出现,则 ok\text{ok} ,若在树上存在,在 mapmap 中出现过,则 repeat\text{repeat} ,若在树上不存在,则 wrong\text{wrong}

    #include <bits/stdc++.h>
    
    using namespace std;
    
    using ll = long long;
    
    constexpr int N = 500010;
    
    signed main()
    {
        int n;
        cin >> n;
    
        int idx = 0;
        vector<int> cnt(N);
        vector ch(N, vector<int>(26));
    
        auto insert = [&](string s) {
            int p = 0;
            for(int i = 0; i < s.size(); i ++) {
                int j = s[i] - 'a';
                if(!ch[p][j]) ch[p][j] = ++idx;
                p = ch[p][j];
            } 
            cnt[p] ++;
        };
    
        auto query = [&](string s) {
            int p = 0;
            for(int i = 0; i < s.size(); i ++) {
                int j = s[i] - 'a';
                if(!ch[p][j]) return 0;
                p = ch[p][j]; 
            }
            return cnt[p];
        };
    
        while(n --) {
            string s;
            cin >> s;
    
            insert(s);
        }   
    
        map<string, int> ma;
    
        int m;
        cin >> m;
        while(m --) {
            string s;
            cin >> s;
            int f = query(s);
            // cout << f << endl;
            if(f != 0 && ma[s] == 0) {
                cout << "OK" << endl;
            } else if(f != 0 && ma[s] != 0) {
                cout << "REPEAT" << endl;
            } else if(f == 0) {
                cout << "WRONG" << endl;
            }
            ma[s] ++;
        }
    }
    

    ps:如果不熟悉 mapmap 可以做下洛谷上的 这道题

    • 1

    信息

    ID
    95
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    24
    已通过
    9
    上传者