zoukankan      html  css  js  c++  java
  • 2017 多校6 String

    多校6 String(ac自动机)

    题意:

    给一本有(n)个单词的字典

    (q)个查询 (pref_i,suff_i) 查询字典里有多少单词前缀匹配(pref_i),后缀同时匹配(suff_i),并且
    (pref_i)(suf_i)不相交

    (0 < n ,q <= 1e5)
    $ sum (|pref_i| + |suff_i|) <= 5e5$
    $ sum |w_i| <= 5e5$
    保证每组查询的前后缀不相交

    思路:

    forever97大神的这个思路很不错,比起题解的做法来说,更加符合字符串的套路吧

    所有查询一起处理,把查询按$suff_i $ * $pref_i $,
    中间用*隔开的形式拼接起来,丢到ac自动机里
    然后对于字典里的每个单词 扩展成两倍,同样中间用 * 隔开,
    在ac自动机里查询有多少个前后缀是该字符串的子串,比较一下长度就可以知道前后缀是否相交

    这样就变成了最简单的在一个文本串中找哪些字符串出现过的问题了

    #include<bits/stdc++.h>
    #define LL long long
    #define P pair<int,int>
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define ls rt<<1
    #define rs (rt<<1|1)
    using namespace std;
    int read(){
        int x = 0;
        char c = getchar();
        while(c < '0' || c > '9') c = getchar();
        while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
        return x;
    }
    const int SIZE = 27;
    const int MAXNODE = 1e6 + 10;
    const int N = 1e6 + 10;
    char word[N],wo[N];
    char pre[N],suf[N];
    int w_len[N];
    int pos[N];
    int ans[N];
    struct AC{
        int ch[MAXNODE][SIZE];
        int f[MAXNODE],last[MAXNODE],val[MAXNODE],length[MAXNODE];
        int sz;
        void init(){sz = 1;memset(ch[0],0,sizeof(ch[0]));length[0]=0;}
        int idx(char c){return c - 'a';}
        int _insert(char *s,int v){
            int u = 0,len = strlen(s);
            for(int i = 0;i < len;i++){
                int c = idx(s[i]);
                if(!ch[u][c]){
                    memset(ch[sz],0,sizeof ch[sz]);
                    val[sz] = 0;
                    length[sz] = i + 1;
                    ch[u][c] = sz++;
                }
                u = ch[u][c];
            }
            if(!val[u]) {ans[u] = 0,val[u] = v;}
            return u;
        }
        void getFail(){
            queue<int> q;
            f[0] = 0;
            for(int c = 0;c < SIZE;c++){
                int u = ch[0][c];
                if(u){
                    f[u] = 0;
                    q.push(u);
                    last[u] = 0;
                }
            }
            while(!q.empty()){
                int r = q.front();q.pop();
                for(int c = 0;c < SIZE;c++){
                    int u = ch[r][c];
                    if(!u){ch[r][c] = ch[f[r]][c];continue;}
                    q.push(u);
                    int v = f[r];
                    while(v && !ch[v][c]) v = f[v];
                    f[u] = ch[v][c];
                    last[u] = val[f[u]]?f[u]:last[f[u]];
                }
            }
        }
        void cal(int len,int u){
            if(u) {
                if(length[u] <= len)   ans[u]++;
                cal(len,last[u]);
            }
        }
        void Find(char *s,int l){
            int len = strlen(s),u = 0;
            for(int i = 0;i < len;i++){
                u = ch[u][idx(s[i])];
                if(val[u]) cal(l,u);
                else if(last[u]) cal(l,last[u]);
            }
        }
    }ac;
    int main(){
    
        int T,n,q;
        T = read();
        while(T--){
           n = read(),q = read();
           int total = 0;
           for(int i = 1;i <= n;i++){
            scanf("%s",wo);
            w_len[i] = strlen(wo);
            for(int j = 0;j < w_len[i];j++) word[j + total] = wo[j];
            total += w_len[i];
           }
           ac.init();
           for(int i = 1;i <= q;i++){
                scanf("%s%s",pre,suf);
                int l = strlen(pre),r = strlen(suf);
                suf[r]='z'+1;
                for(int j = 0;j < l;j++) suf[r+1+j] = pre[j];
                suf[l +  r + 1] = '';
                pos[i] = ac._insert(suf,i);
           };
           ac.getFail();
           int now = 0;
           for(int i = 1;i <= n;i++) {
                for(int j = 0;j < w_len[i];j++) pre[j + w_len[i] + 1] = pre[j] = word[now + j];
                pre[w_len[i]] = 'z' + 1;
                pre[2 * w_len[i] + 1] = '';
                ac.Find(pre,w_len[i]+1);
                now += w_len[i];
           }
           for(int i = 1;i <= q;i++) printf("%d
    ",ans[pos[i]]);
        }
        return 0;
    }
    
    
    
  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/jiachinzhao/p/7353259.html
Copyright © 2011-2022 走看看