zoukankan      html  css  js  c++  java
  • [poj 3693]后缀数组+出现次数最多的重复子串

    题目链接:http://poj.org/problem?id=3693

    枚举长度L,看长度为L的子串最多能重复出现几次,首先,能出现1次是肯定的,然后看是否能出现两次及以上。由抽屉原理,这个子串出现次数>=2,那么必定会覆盖s[0],s[L],s[2L],...中相邻的两个,枚举是哪两个。对于覆盖了这两个的重复子串,它重复的次数就是看这两个后缀向前向后各自最多能匹配到多远。假设向前向后共匹配了长度K,那么重复的次数就是K/L+1。

    这里有3个问题.

    第一个,为什么先前向后各自匹配就可以了?因为子串长度就是L,枚举的这两个位置的距离也是L,那么这两个位置必定得是相同的。

    第二个问题,怎么看向前最多匹配多少?向后的话直接通过height数组即可实现,而向前呢?难道要倒着再做一次后缀数组?(当然也不是不可行)事实上不用这么麻烦,直接考虑我们要求的结果K/L+1。利用整除的特性其实很容易得到这个结果。比如我们向后最多匹配L1。现在我们想知道结果能不能比L1/L+1来的大,怎么办呢?考虑L-L1%L,这个数代表L1至少要加几,才会让结果有所增加。那么显然向前距离在这个数以内的我们都不用检验了,因为即使检验到能匹配也没啥用,对结果没啥影响。所以我们检验一下距离是这个数的两个后缀的匹配长度,如果能匹配就更新一下结果。那么距离在这个数以外的呢?其实也不用检验了,如果说距离在这个数以外的还能让结果增加,那必须得再+L,再+L的话其实不必检验了,因为如果能匹配,在之前的求解(上次的枚举)中已经检验过了。因此,只检验一个位置即可。

    第三个问题,怎么得到字典序最小的那一组解。通过sa数组的顺序枚举。不过得稍微优化一下,不能所有长度都尝试,不然会T。

    穷举长度 L 的时间是 n,每次计算的时间是 n/L。所以整个做法的时间复杂度是 O(n/1+n/2+n/3+……+n/n)=O(nlogn)。(假设查询最长公共前缀的复杂度是O(1),用rmq预处理可以做到)

    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    const int MAXN = 100010;
    #define F(x) ((x)/3+((x)%3==1?0:tb))
    #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
    int wa[MAXN*3],wb[MAXN*3],wv[MAXN*3],wss[MAXN*3];
    int c0(int *r,int a,int b)
    {
        return r[a] == r[b] && r[a+1] == r[b+1] && r[a+2] == r[b+2];
    }
    int c12(int k,int *r,int a,int b)
    {
        if(k == 2)
            return r[a] < r[b] || ( r[a] == r[b] && c12(1,r,a+1,b+1) );
        else return r[a] < r[b] || ( r[a] == r[b] && wv[a+1] < wv[b+1] );
    }
    void sort(int *r,int *a,int *b,int n,int m)
    {
        int i;
        for(i = 0; i < n; i++)wv[i] = r[a[i]];
        for(i = 0; i < m; i++)wss[i] = 0;
        for(i = 0; i < n; i++)wss[wv[i]]++;
        for(i = 1; i < m; i++)wss[i] += wss[i-1];
        for(i = n-1; i >= 0; i--)
            b[--wss[wv[i]]] = a[i];
    }
    void dc3(int *r,int *sa,int n,int m)
    {
        int i, j, *rn = r + n;
        int *san = sa + n, ta = 0, tb = (n+1)/3, tbc = 0, p;
        r[n] = r[n+1] = 0;
        for(i = 0; i < n; i++)if(i %3 != 0)wa[tbc++] = i;
        sort(r + 2, wa, wb, tbc, m);
        sort(r + 1, wb, wa, tbc, m);
        sort(r, wa, wb, tbc, m);
        for(p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++)
            rn[F(wb[i])] = c0(r, wb[i-1], wb[i]) ? p - 1 : p++;
        if(p < tbc)dc3(rn,san,tbc,p);
        else for(i = 0; i < tbc; i++)san[rn[i]] = i;
        for(i = 0; i < tbc; i++) if(san[i] < tb)wb[ta++] = san[i] * 3;
        if(n % 3 == 1)wb[ta++] = n - 1;
        sort(r, wb, wa, ta, m);
        for(i = 0; i < tbc; i++)wv[wb[i] = G(san[i])] = i;
        for(i = 0, j = 0, p = 0; i < ta && j < tbc; p++)
            sa[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++];
        for(; i < ta; p++)sa[p] = wa[i++];
        for(; j < tbc; p++)sa[p] = wb[j++];
    }
    void da(int str[],int sa[],int rank[],int height[],int n,int m)
    {
        for(int i = n; i < n*3; i++)
            str[i] = 0;
        dc3(str, sa, n+1, m);
        int i,j,k = 0;
        for(i = 0; i <= n; i++)rank[sa[i]] = i;
        for(i = 0; i < n; i++)
        {
            if(k) k--;
            j = sa[rank[i]-1];
            while(str[i+k] == str[j+k]) k++;
            height[rank[i]] = k;
        }
    }
    
    int str[MAXN*3],sa[MAXN*3],rk[MAXN],height[MAXN];
    int RMQ[MAXN];
    int mm[MAXN];
    int best[20][MAXN];
    void initRMQ(int n)
    {
        mm[0]=-1;
        for(int i=1; i<=n; i++)
            mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
        for(int i=1; i<=n; i++)best[0][i]=i;
        for(int i=1; i<=mm[n]; i++)
            for(int j=1; j+(1<<i)-1<=n; j++)
            {
                int a=best[i-1][j];
                int b=best[i-1][j+(1<<(i-1))];
                if(RMQ[a]<RMQ[b])best[i][j]=a;
                else best[i][j]=b;
            }
    }
    int askRMQ(int a,int b)
    {
        int t;
        t=mm[b-a+1];
        b-=(1<<t)-1;
        a=best[t][a];
        b=best[t][b];
        return RMQ[a]<RMQ[b]?a:b;
    }
    int lcp(int a,int b)
    {
        a=rk[a];
        b=rk[b];
        if(a>b)swap(a,b);
        return height[askRMQ(a+1,b)];
    }
    
    char s[MAXN];
    int cou;
    int l;
    int a[MAXN];
    int cnt=0;
    
    int main()
    {
        int cas=0;
        while (~scanf("%s",s) && s[0]!='#')
        {
            l=strlen(s);
            for (int i=0; i<l; i++) str[i]=s[i]-'a'+1;
            str[l]=0;
            da(str,sa,rk,height,l,30);
    //        for (int i=0;i<l;i++) printf("%d ",rk[i]);
            for (int i=1;i<=l;i++) RMQ[i]=height[i];
            initRMQ(l);
            cou=1;
            cnt=0;
            a[cnt++]=1;
            for (int L=1; L<=l/2; L++)
            {
                for (int j=0; j+L<l; j+=L)
                {
                    int back=lcp(j,j+L);
    //                printf(":%d:
    ",back);
                    if (back/L+1>cou)
                    {
                        cou=back/L+1;
                        cnt=0;
                        a[cnt++]=L;
                    }
                    else if (back/L+1==cou) a[cnt++]=L;
                    int check=L-back%L;
                    if (check!=L && j-check>=0)
                    {
                        int pre=lcp(j-check,j-check+L);
    //                    printf(":%d:
    ",pre);
                        if (pre/L+1>cou)
                        {
                            cou=pre/L+1;
                            cnt=0;
                            a[cnt++]=L;
                        }
                        else if (pre/L+1==cou) a[cnt++]=L;
                    }
                }
            }
    //        printf("::%d
    ",cou);
            int ansp=-1;
            int ansl=-1;
    //        for (int i=1;i<=l;i++) printf("%d ",sa[i]);
            for (int i=1; i<=l&&ansp==-1; i++)
            {
                int j=sa[i];
                for (int k=0; k<cnt&&a[k]<=(l-j)/cou; k++)
                {
                    if (j+a[k]<l && lcp(j,j+a[k])/a[k]+1>=cou)
                    {
    //                    printf("QAQ
    ");
                        ansp=j;
                        ansl=a[k];
                        break;
                    }
                }
            }
            s[ansp+ansl*cou]=0;
    //        printf("%d %d
    ",ansp,ansl);
            printf("Case %d: %s
    ",++cas,s+ansp);
        }
        return 0;
    }
  • 相关阅读:
    给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
    11
    实战 迁移学习 VGG19、ResNet50、InceptionV3 实践 猫狗大战 问题
    tx2系统备份与恢复
    如何在Ubuntu 18.04上安装和卸载TeamViewer
    bzoj 3732 Network (kruskal重构树)
    bzoj2152 聪聪可可 (树形dp)
    牛客 216D 消消乐 (二分图最小点覆盖)
    牛客 197E 01串
    Wannafly挑战赛23
  • 原文地址:https://www.cnblogs.com/acmsong/p/7302505.html
Copyright © 2011-2022 走看看