zoukankan      html  css  js  c++  java
  • BZOJ 3473 字符串

    题目大意:

    给你(n)个字符串, 询问每个串中有多少个子串, 满足它至少是(k)个字符串的子串.
    数据范围: 所有字符串总长度(ge 100000)

    Solution

    我们对所有字符串建立广义后缀自动机. DFS一次, 启发式合并每个点被哪些串包含, 这样我们就可以得到一个点是否为大于等于(k)个字符串的子串.
    考虑如何统计答案: 我们把每个字符串放入后缀自动机中跑一次, 假如当前节点不满足是至少(k)个字符串的子串, 则往父亲节点跳, 直到满足要求或跳到根节点; 然后在这个串的答案上加上当前节点表示的最大长度即可, 表示有这么多个以当前位置结尾的串满足要求.

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <set>
    #include <algorithm>
    #define vector std::vector
    #define set std::set
    #define swap std::swap
    
    const int LEN = (int)1e5;
    int k;
    int ans[LEN + 1];
    struct suffixAutomaton
    {
        struct node
        {
            node *suc[26], *pre; vector<node*> successorOnSuffixTree;
            int len, vst, tg; vector<int> bck;
            inline node() {for(int i = 0; i < 26; ++ i) suc[i] = NULL; bck.clear(); successorOnSuffixTree.clear(); vst = tg = 0 ;}
        }*rt;
        inline suffixAutomaton() {rt = new node; rt->len = 0; rt->pre = NULL;}
        inline void insert(char *str, int len, int id)
        {
            node *lst = rt;
            for(int i = 0; i < len; ++ i)
            {
                int c = str[i] - 'a';
                if(lst->suc[c] != NULL)
                {
                    node *p = lst->suc[c];
                    if(p->len == lst->len + 1) {p->bck.push_back(id); lst = p; continue;}
                    node *q = new node; *q = *p; q->len = lst->len + 1; q->bck.push_back(id);
                    p->pre = q;
                    for(; lst != NULL && lst->suc[c] == p; lst = lst->pre) lst->suc[c] = q;
                    lst = q;
                    continue;
                }
                node *u = new node; u->len = lst->len + 1; u->bck.push_back(id);
                for(; lst != NULL && lst->suc[c] == NULL; lst = lst->pre) lst->suc[c] = u;
                if(lst == NULL) u->pre = rt;
                else
                {
                    node *p = lst->suc[c];
                    if(p->len == lst->len + 1) u->pre = p;
                    else
                    {
                        node *q = new node; *q = *p; q->len = lst->len + 1;
                        p->pre = u->pre = q;
                        for(; lst != NULL && lst->suc[c] == p; lst = lst->pre) lst->suc[c] = q;
                    }
                }
                lst = u;
            }
        }
        void build(node *u)
        {
            if(u->pre != NULL) u->pre->successorOnSuffixTree.push_back(u), u->vst = 1;;
            for(int i = 0; i < 26; ++ i) if(u->suc[i] != NULL && ! u->suc[i]->vst) build(u->suc[i]);
        }
        inline void buildSuffixTree() {build(rt);}
        set<int> DFS(node *u)
        {
            set<int> st; st.clear(); for(auto id : u->bck) st.insert(id);
            for(auto v : u->successorOnSuffixTree)
            {
                set<int> res = DFS(v);
                if(st.size() < res.size()) swap(st, res);
                for(auto id : res) st.insert(id);
            }
            if(st.size() >= k) u->tg = 1;
            return st;
        }
        inline void work() {DFS(rt);}
        inline int getAnswer(char *str, int len)
        {
            node *u = rt; int res = 0;
            for(int i = 0; i < len; ++ i)
            {
                u = u->suc[str[i] - 'a'];
                while(u != rt && ! u->tg) u = u->pre;
                res += u->len;
            }
            return res;
        }
    }SAM;
    int main()
    {
    
        #ifndef ONLINE_JUDGE
    
        freopen("BZOJ3473.in", "r", stdin);
        freopen("BZOJ3473.out", "w", stdout);
    
        #endif
    
        int n; scanf("%d%d", &n, &k);
        static char str[LEN]; static int len[LEN]; int p = 0;
        for(int i = 0; i < n; ++ i) scanf("%s", str + p), SAM.insert(str + p, len[i] = strlen(str + p), i), p += len[i];
        SAM.buildSuffixTree();
        memset(ans, 0, sizeof(ans));
        SAM.work();
        p = 0;
        for(int i = 0; i < n; ++ i) printf("%d
    ", SAM.getAnswer(str + p, len[i])), p += len[i];
    }
    
    

    这里补充一些关于广义后缀自动机的理解.
    我们知道, 普通后缀自动机的每个节点表示的是结束位置相同的一些子串的集合; 而在广义后缀自动机中, 这里的结束位置应该被看作是一个二元组, 同时记录了是哪一个串的哪一个位置.
    广义后缀树中每个点所表示的字符串的出现次数等于以它为根的子树中实点个数, 这与普通后缀自动机中是一样的.
    广义后缀自动机的构造分为在线和离线两种; 这里只讲在线的: 我们在插入的时候要分类讨论, 假如已经有当前字母对应的子节点, 则判断这个子节点的长度是否上一个点的长度加1, 假如是则直接跳到这个子节点即可; 否则新建一个节点, 同时跳parent, 修改路径.

  • 相关阅读:
    el-select下拉框选项太多导致卡顿,使用下拉框分页来解决
    vue+elementui前端添加数字千位分割
    Failed to check/redeclare auto-delete queue(s)
    周末啦,做几道面试题放松放松吧!
    idea快捷键
    解决flink运行过程中报错Could not allocate enough slots within timeout of 300000 ms to run the job. Please make sure that the cluster has enough resources.
    用.net平台实现websocket server
    MQTT实战3
    Oracle 查看当前用户下库里所有的表、存储过程、触发器、视图
    idea从svn拉取项目不识别svn
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7414126.html
Copyright © 2011-2022 走看看