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;
    }
  • 相关阅读:
    Android四大组件之BroadcastReceiver
    Android四大组件之Activity详解
    2017-2018-1 20155307 《信息安全系统设计基础》第十周课上未完成补充以及课下IPC作业
    第八周课上额外项目:pwd的实现
    20155307《信息安全系统设计基础》第9周学习总结
    2017-2018-11 20155307刘浩 20155338常胜杰 20155335俞昆 实验三 实时系统实验报告
    20155307 刘浩 信息安全技术(李冬冬) 实验三 数字证书应用 实验报告
    2017-2018-1 20155307 《信息安全系统设计基础》第七周学习总结
    实验二 20155307 20155335 20155338 实验报告 固件程序设计
    (完整!)2017-11-1 第七次 信息安全系统设计基础课 主要内容
  • 原文地址:https://www.cnblogs.com/acmsong/p/7302505.html
Copyright © 2011-2022 走看看