zoukankan      html  css  js  c++  java
  • 洛谷

    https://www.luogu.org/problemnew/show/P3966
    因为文本串就是字典本身,所以这个和平时的AC自动机不太一样。平时的query要沿着fail树把子树的出现次数依次统计。但是这个的query就是对每个字典里的字符串搞一次。所以就直接按广搜的顺序反过来树形dp统计出子树中的出现次数,直接回答。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int MAXN = 1e6;
    const int MAXM = 2e2;
    
    int idx[MAXM + 5];
    
    struct Trie {
        int trie[MAXN + 5][26], fail[MAXN + 5], cnt[MAXN + 5], que[MAXN + 5];
        int root, top;
        int newnode() {
            for(int i = 0; i < 26; i++)
                trie[top][i] = -1;
            cnt[top++] = 0;
            return top - 1;
        }
    
        void init() {
            top = 0;
            root = newnode();
        }
    
        void insert(char buf[], int id) {
            int len = strlen(buf);
            int cur = root;
            for(int i = 0; i < len; i++) {
                int u = buf[i] - 'a';
                if(trie[cur][u] == -1)
                    trie[cur][u] = newnode();
                cur = trie[cur][u];
                cnt[cur]++;
            }
            idx[id] = cur;
        }
    
        void build() {
            int head = 0, tail = 0;
            fail[root] = root;
            for(int i = 0; i < 26; i++) {
                if(trie[root][i] == -1)
                    trie[root][i] = root;
                else {
                    fail[trie[root][i]] = root;
                    que[tail++] = trie[root][i];
                }
            }
            while(head < tail) {
                int cur = que[head++];
                for(int i = 0; i < 26; i++) {
                    if(trie[cur][i] == -1)
                        trie[cur][i] = trie[fail[cur]][i];
                    else {
                        fail[trie[cur][i]] = trie[fail[cur]][i];
                        que[tail++] = trie[cur][i];
                    }
                }
            }
            for(int i = tail - 1; i >= 0; i--) {
                cnt[fail[que[i]]] += cnt[que[i]];
            }
        }
    };
    
    char buf[MAXN + 5];
    
    Trie ac;
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
        //freopen("Yinku.out","w",stdout);
    #endif // Yinku
        int n;
        scanf("%d", &n);
        ac.init();
        for(int i = 0; i < n; i++) {
            scanf("%s", buf);
            ac.insert(buf, i);
        }
        ac.build();
        for(int i = 0; i < n; i++)
            printf("%d
    ", ac.cnt[idx[i]]);
    }
    

    这种写法是,每次文本串遇到一个模式串,就会给这个模式串+1次出现次数。问题是怎么优化重复的文本串呢?把文本串插入另一个trie里面就可以知道他们的重复数。然后每次就给每个模式串都贡献一个重数就等价了。这样最好理解。优化的地方在于同样的文本串不用多次匹配。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int MAXN = 1e6;
    const int MAXM = 2e2;
    
    int ans[MAXM + 5];
    int aidx[MAXN + 5][MAXM + 5];
    
    struct Trie {
        int trie[MAXN + 5][26], fail[MAXN + 5], cnt[MAXN + 5], que[MAXN + 5];
        //本题多使用的一个是文本串重复计数器tcnt,但是在这里文本一定和模式一样就可以合并到模式重复计数器cnt里,一个文本防重vis
        int tcnt[MAXN + 5];
        bool vis[MAXN + 5];
    
        int root, top;
        int newnode() {
            for(int i = 0; i < 26; i++)
                trie[top][i] = -1;
            cnt[top++] = 0;
            return top - 1;
        }
    
        void init() {
            top = 0;
            root = newnode();
        }
    
        void insert(char buf[], int id) {
            int len = strlen(buf);
            int cur = root;
            for(int i = 0; i < len; i++) {
                int u = buf[i] - 'a';
                if(trie[cur][u] == -1)
                    trie[cur][u] = newnode();
                cur = trie[cur][u];
            }
            cnt[cur]++;
            //本题中文本可以一并插入,干脆合并成一个cnt,可以在同一个trie中,否则应该分开插入两个trie
            aidx[cur][cnt[cur]-1] = id;
        }
    
        void build() {
            int head = 0, tail = 0;
            fail[root] = root;
            for(int i = 0; i < 26; i++) {
                if(trie[root][i] == -1)
                    trie[root][i] = root;
                else {
                    fail[trie[root][i]] = root;
                    que[tail++] = trie[root][i];
                }
            }
            while(head < tail) {
                int cur = que[head++];
                for(int i = 0; i < 26; i++) {
                    if(trie[cur][i] == -1)
                        trie[cur][i] = trie[fail[cur]][i];
                    else {
                        fail[trie[cur][i]] = trie[fail[cur]][i];
                        que[tail++] = trie[cur][i];
                    }
                }
            }
        }
    
        void query(char buf[]) {
            //该文本串匹配了多少次模式串
            /*int len=strlen(buf);
            int cur=root;
            int res=0;
            for(int i=0;i<len;i++){
                cur=trie[cur][buf[i]-'a'];
                int temp=cur;
                while(temp!=root){
                    //这里既然是+=意思就是有多少个模式串匹配了都算,而且多次匹配就多次算,否则每次就把他清0就可以了
                    res+=cnt[temp];
                    temp=fail[temp];
                }
            }
            return res;*/
            //该种文本串对所有模式串贡献了几次?
            //插好所有文本之后得到文本的重数,对每个文本在trie中O(n)找出他的重数以及他有没有被vis
            //没有被vis的话就对每个匹配的模式贡献对应的重数
            int len = strlen(buf);
            int cur = root;
            for(int i = 0; i < len; i++) {
                cur = trie[cur][buf[i] - 'a'];
            }
            if(vis[cur])
                return;
            int cf = cnt[cur];
            vis[cur]=1;
    
            cur = root;
            for(int i = 0; i < len; i++) {
                cur = trie[cur][buf[i] - 'a'];
                int temp = cur;
                while(temp != root) {
                    for(int j = 0; j < cnt[temp]; j++) {
                        ans[aidx[temp][j]]+=cf;
                    }
                    temp = fail[temp];
                }
            }
        }
    };
    
    char buf[MAXM+5][MAXN + 5];
    
    Trie ac;
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
        //freopen("Yinku.out","w",stdout);
    #endif // Yinku
        int n;
        scanf("%d", &n);
        ac.init();
        for(int i = 0; i < n; i++) {
            scanf("%s", buf[i]);
            ac.insert(buf[i], i);
        }
        ac.build();
        for(int i = 0; i < n; i++) {
            ac.query(buf[i]);
        }
    
        for(int i = 0; i < n; i++)
            printf("%d
    ", ans[i]);
    }
    
  • 相关阅读:
    Tapestry5 Setup with HTML5 and jQuery
    CSS学习六:布局剖析
    CSS学习三:表格,表单,超链接和鼠标等。
    CSS+DIV实战
    一到关于c++继承和多态的题
    一道有关指针的题
    小程序,嘿嘿
    《犯罪心理》名言部分
    ethereal与wincap
    并发编程之:Lock
  • 原文地址:https://www.cnblogs.com/Yinku/p/11084288.html
Copyright © 2011-2022 走看看