zoukankan      html  css  js  c++  java
  • BZOJ 3277 串 & BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析)

    标签那么长是因为做法太多了。。。

    题目链接: (bzoj 3277) https://www.lydsy.com/JudgeOnline/problem.php?id=3277

    (bzoj 3473) https://www.lydsy.com/JudgeOnline/problem.php?id=3473

    题解:

    先讲三个做法公共部分: 建出广义SAM,然后对于每个点求出它在多少字符串中出现过。

    做法一

    把每个字符串在广义SAM上暴力跑。每跑到一个点就暴力沿着fail树往上跳,标记跳过的点,直到跳到已标记的点为止(每个串要换用不同的标记)。

    时间复杂度(O(Lsqrt L)) ((n)为串的个数,(L)为总长度)

    写一下时间复杂度分析: (我自己想的,很有可能是错的,有错恳请大佬指出!!感谢)

    假设某个字符串长度为(x), 则最坏情况下它一直在往深处走,并且每一步都没有碰到已经跳过的点,这种情况下其走的步数是(sum^{x}_{i=1}i=O(x^2)).

    但是它还要受到另一个限制,就是走的步数不超过SAM总大小(O(L)). 因此其对时间复杂度贡献为(O(min(x^2),L)).

    计算最坏情况下的时间复杂度,也就是已知(sum^{m}_{i=1} x_i=L), 求(sum^{m}_{i=1} min(x_i^2,L))的最大值。显然当(x_i>sqrt L)时是没有任何意义的(白白浪费代价,价值不增加),所以就是已知(sum^{m}_{i=1} x_i=L)且对于任意(i)(ile sqrt L), 求(sum^{m}_{i=1} x_i^2)的最大值。由函数的凹凸性知显然(或者也可以用偏导数解释,如果你愿意的话。。。)所有串长均为(sqrt L)时目标函数最大,为(O(Lsqrt L)).

    做法二

    类似于BZOJ2754/BZOJ2780, 就是个数颜色问题,每个点开个set记录经过这个点的所有串,然后沿着Parent树自下而上启发式合并。时间复杂度(O(nlog^2n)).

    线段树合并貌似可以做到(O(nlog n))?

    做法三

    依然是数颜色,可以使用DFS序+主席树等各种神奇做法解决。时间复杂度(O(nlog^2n)) (?)

    代码

    做法一

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<vector>
    #define llong long long
    using namespace std;
     
    const int N = 4e5;
    const int S = 26;
    struct SAM
    {
        int id[N+3];
        int fa[N+3];
        int son[N+3][S+3];
        int len[N+3];
        int sz[N+3];
        int buc[N+3];
        int oid[N+3];
        int cnt[N+3];
        int cid[N+3];
        int mx[N+3];
        int siz,rtn,lstpos;
        void init() {id[0] = siz = rtn = lstpos = 1;}
        int insertstr(char ch)
        {
            int p = lstpos,np; siz++; np = lstpos = siz; len[np] = len[p]+1; sz[np] = 1;
            for(; p && son[p][ch]==0; p=fa[p]) {son[p][ch] = np;}
            if(p==0) fa[np] = rtn;
            else
            {
                int q = son[p][ch];
                if(len[q]==len[p]+1) {fa[np] = q;}
                else
                {
                    siz++; int nq = siz; len[nq] = len[p]+1;
                    memcpy(son[nq],son[q],sizeof(son[q]));
                    fa[nq] = fa[q]; fa[np] = fa[q] = nq;
                    for(; p && son[p][ch]==q; p=fa[p]) {son[p][ch] = nq;}
                }
            }
            return np;
        }
        void getsort()
        {
            for(int i=1; i<=siz; i++) buc[len[i]]++;
            for(int i=1; i<=siz; i++) buc[i] += buc[i-1];
            for(int i=siz; i>=1; i--) oid[buc[len[i]]--] = i;
        }
    } sam;
    vector<char> str[N+3];
    char a[N+3];
    int n,m;
     
    int main()
    {
        scanf("%d%d",&n,&m);
        sam.init();
        for(int i=1; i<=n; i++)
        {
            scanf("%s",a+1);
            sam.lstpos = 1;
            int lena = strlen(a+1);
            for(int j=1; j<=lena; j++) {sam.insertstr(a[j]-96); str[i].push_back(a[j]);}
        }
    //  for(int i=1; i<=sam.siz; i++) for(int j=1; j<=S; j++) if(sam.son[i][j]) printf("trans%d %d %d
    ",i,j,sam.son[i][j]);
    //  for(int i=1; i<=sam.siz; i++) printf("i%d len%d fa%d
    ",i,sam.len[i],sam.fa[i]);
        sam.getsort();
        for(int i=1; i<=n; i++)
        {
            int pos = sam.rtn;
            for(int j=0; j<str[i].size(); j++)
            {
                pos = sam.son[pos][str[i][j]-96];
                int tmp = pos;
                for(; tmp && sam.cid[tmp]!=i; tmp=sam.fa[tmp]) {sam.cnt[tmp]++; sam.cid[tmp] = i;}
            }
        }
        sam.cnt[1] = 0;
        for(int i=1; i<=sam.siz; i++)
        {
            int u = sam.oid[i];
            sam.mx[u] = sam.mx[sam.fa[u]]+(sam.cnt[u]>=m ? sam.len[u]-sam.len[sam.fa[u]] : 0);
    //      printf("%d mx%d
    ",u,sam.mx[u]);
        }
        for(int i=1; i<=n; i++)
        {
            int pos = sam.rtn; llong ans = 0ll;
            for(int j=0; j<str[i].size(); j++)
            {
                pos = sam.son[pos][str[i][j]-96];
                ans += (llong)sam.mx[pos];
    //          putchar(str[i][j]); printf("pos%d
    ",pos); puts("");
            }
            printf("%lld ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    day96-luffy项目-首页轮播图异步更新、课程页面分析与搭建、课程分类群查接口、 课程群查接口、搜索组件和过滤、自定义过滤类
    day94-路飞项目-短信接口频率限制、验证码登陆接口、前台发送验证码 、前台验证码登陆、后台注册接口 、前台注册功能、redis
    day95-Redis操作、管道、celery的使用
    day93-路飞项目-登陆注册模态框、登陆注册接口分析、多种登陆方式、cookies修改页面登陆状态、前台注销 、手机号是否存在接口、腾讯云短信服务、短信验证码接口
    TensorFlow 系列一(在Windows下的安装)
    Matlab中plot基本用法
    Socket (二) 简单的代码实现
    LIBSVM (四) SVM 的参数优化(交叉验证)
    Socket (一) 基础及接口函数
    LIBSVM (三) 葡萄酒种类识别
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11073773.html
Copyright © 2011-2022 走看看