poj3693 ,spoj687 重复次数最多的连续重复子串 后缀数组

本文介绍了一种寻找字符串中最长重复子串的高效算法,并通过两道编程题详细阐述了解决方案的实现细节,包括如何利用后缀数组和LCP数组进行快速匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      给一个字符串,给定一个字符串,求重复次数最多的连续重复子串。

      09年罗穗骞的论文里有讲这题,先引用一下论文里的讲解:

       先穷举长度L,然后求长度为L 的子串最多能连续出现几次。首先连续出现1 次是肯定可以的,所以这里只考虑至少2 次的情况。假设在原字符串中连续出现2 次,记这个子字符串为S,那么S 肯定包括了字符r[0], r[L], r[L*2],r[L*3], ……中的某相邻的两个。所以只须看字符r[L*i]和r[L*(i+1)]往前和往后各能匹配到多远,记这个总长度为K,那么这里连续出现了K/L+1 次。最后看最大值是多少。如图7 所示。

                                                                 


穷举长度L 的时间是n,每次计算的时间是n/L。所以整个做法的时间复杂度是O(n/1+n/2+n/3+……+n/n)=O(nlogn)。

以上内容提供了一个思路方向,但剩下的东西也挺难想的...论文里只讲了右面匹配的情况,队向左匹配的方法并没有细说(难道是认为太简单了么....= =.....),举个例子吧(数据是从poj discuss里搬来的)

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

b a c c d  b a c c d  b   a    c    b   d    b    a   c   b   d

加入我现在枚举到了l=5,p1=5,p2=10

令k=LCS(p1,p2)=3(bac),

而重复的次数即c=k/l+1=1;

由于本题要输出字典序最小的答案,所以p1-l+1 -----p1-1之间任何一个位置都可能是我们要的答案,所以我们必须在字符匹配的情况下逐位向前平移去找答案,

移动两位后,p1=3,p2=8,此时k也增加2等于5,发现达到了l,所以此时c++,等于2,之后发现再往前平移字符依然是匹配的,所以继续,找到p1=1,p2=6,此时k=7,c=2,重复的次数和p1在3的时候没有变化,但显然此时的字典序是更小的,所以在这里更新答案的位置,下一位字符不再匹配,所以到此结束。

这样按照论文中的做法,把长度枚举一边即可得到答案。要说一下的是,这么去找最小的字典序的话,最差的情况其实还是n^2的(比如构造50000个a,跑出来结果就要将近10s),可能是数据给的太弱了(网上搜了几个题解,好多代码都被discuss里给的数据cha掉了...),这么写再poj上只用了400ms。我也没什么别的好方法了...还望大神赐教...



spoj687这题就好办了,只要求输出最大的重复次数,和上面的方法一样。枚举长度,然后枚举i*l,(i+1)*l的位置,由于不求字符串,这里我们便可以省去平移。求出k之后,若k%l>0的话,说明p1,p2之前的位置也可能有匹配的情况,所以找到p3=p1-(l-k%l),若此时LCS(p3,p3+l)>k,那么说明前面这l-k%l位也是匹配的,所以k+=(l-k%l)。此时c=k/l+1就是我们要的答案。

贴两个代码了,

poj3693

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=220100;
int s[maxn],ss[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int sa2[maxn];
char s1[maxn],s2[maxn];
int rank[maxn],height[maxn];
int rank2[maxn],height2[maxn];
int l1,l2;
int len,len1;
int n,m;
inline int idx(char s)
{
   return s-'a'+1;
}

void getheight(int n)
{
    int i,j,k=0;
    for (i=0; i<=n; i++) rank[sa[i]]=i;

    for (i=0; i<n; i++)
    {
        if (k) k--;
        int j=sa[rank[i]-1];
        while(s[i+k]==s[j+k]) k++;
        height[rank[i]]=k;
    }
}

void build_ss(int m,int n)
{
    n++;
    int i,*x=t,*y=t2;
    for (int i=0; i<m; i++) c[i]=0;
    for (int i=0; i<n; i++) c[x[i]=s[i]]++;
    for (int i=1; i<m; i++) c[i]+=c[i-1];
    for (int i=n-1; i>=0; i--)
      sa[--c[x[i]]]=i;
    for (int k=1; k<=n; k<<=1)
    {
        int p=0;
        for (i=n-k; i<n; i++) y[p++]=i;
        for (i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;

        for (i=0; i<m; i++) c[i]=0;
        for (i=0; i<n; i++) c[x[y[i]]]++;
        for (i=1; i<m; i++) c[i]+=c[i-1];
        for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for (i=1; i<n; i++)
        x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])? p-1 : p++;
        if (p>=n) break;
        m=p;
    }
}
int d[maxn][22];
void RMQ_init()
{
    for (int i=1; i<=n; i++) d[i][0]=height[i];
     for (int j=1; (1<<j)<=n; j++)
      for (int i=1; (i+(1<<j)-1)<=n; i++)
      d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R)
{
    if (L>R) swap(L,R);
    L++;

    int k=0;
    while((1<<(k+1))<=R-L+1) k++;
    return min(d[L][k],d[R-(1<<k)+1][k]);
}

int main()
{
//    freopen("in.txt","r",stdin);
    int tt=0;
    while(~scanf("%s",s1) && s1[0]!='#')
    {

        n=strlen(s1);
        for (int i=0; i<n; i++)
        s[i]=idx(s1[i]);
        s[n]=0;
        build_ss(30,n);
        getheight(n);
        RMQ_init();
        int ans=0,pos=-1,len=0;
        for (int l=1; l<=n/2; l++)
        {
            for (int i=0; (i+1)*l<n; i++)
            {
                int p1=i*l,p2=(i+1)*l;
                int k=RMQ(rank[p1],rank[p2]);
                int c=k/l+1;
                int r=l-k%l;
                int p=i*l;
                int cnt=0;
                for (int j=p1-1; j>p1-l&&s[j]==s[j+l]&&j>=0; j--)
                {
                    cnt++;
                    if (cnt==r)
                    {
                        c++;
                        p=j;
                    }
                    if (rank[j]<rank[p])
                    p=j;
                }
                if (ans<c)
                {
                    pos=p;
                    ans=c;
                    len=l*c;
                }
                else if (ans==c)
                {
                    if (rank[pos]>rank[p])
                    {
                        pos=p;
                        len=l*c;
                    }

                }
            }
        }
        cout<<"Case "<<++tt<<": ";
        for (int i=0,j=pos; i<len; i++,j++)
        cout<<s1[j];
        cout<<endl;
    }
}

spoj687的话,上面的代码也能过,只不过要跑10s+,若用删掉平移的过程,可以把时间降到4s+

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=120100;
int s[maxn],ss[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
char s1[maxn],s2[maxn];
int rank[maxn],height[maxn];
int l1,l2;
int len,len1;
int n,m;
inline int idx(char s)
{
   return s-'a'+1;
}

void getheight(int n)
{
    int i,j,k=0;
    for (i=0; i<=n; i++) rank[sa[i]]=i;

    for (i=0; i<n; i++)
    {
        if (k) k--;
        int j=sa[rank[i]-1];
        while(s[i+k]==s[j+k]) k++;
        height[rank[i]]=k;
    }
}

void build_ss(int m,int n)
{
    n++;
    int i,*x=t,*y=t2;
    for (int i=0; i<m; i++) c[i]=0;
    for (int i=0; i<n; i++) c[x[i]=s[i]]++;
    for (int i=1; i<m; i++) c[i]+=c[i-1];
    for (int i=n-1; i>=0; i--)
      sa[--c[x[i]]]=i;
    for (int k=1; k<=n; k<<=1)
    {
        int p=0;
        for (i=n-k; i<n; i++) y[p++]=i;
        for (i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;

        for (i=0; i<m; i++) c[i]=0;
        for (i=0; i<n; i++) c[x[y[i]]]++;
        for (i=1; i<m; i++) c[i]+=c[i-1];
        for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for (i=1; i<n; i++)
        x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])? p-1 : p++;
        if (p>=n) break;
        m=p;
    }
}
int d[maxn][22];
void RMQ_init()
{
    for (int i=1; i<=n; i++) d[i][0]=height[i];
     for (int j=1; (1<<j)<=n; j++)
      for (int i=1; (i+(1<<j)-1)<=n; i++)
      d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R)
{
    if (L>R) swap(L,R);
    L++;

    int k=0;
    while((1<<(k+1))<=R-L+1) k++;
    return min(d[L][k],d[R-(1<<k)+1][k]);
}

int main()
{
//    freopen("in.txt","r",stdin);
    int tt=0;
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%d",&n);
        memset(s1,0,sizeof s1);
        for (int i=0; i<n; i++)
        scanf("%s",&s1[i]);
        n=strlen(s1);
//        puts(s1);
        for (int i=0; i<n; i++)
        s[i]=idx(s1[i]);
        s[n]=0;
        build_ss(4,n);
        getheight(n);
        RMQ_init();
        int ans=1,len=0;
        if (n==0) ans=0;
        for (int l=1; l<=n/2; l++)
        {
            for (int i=0; (i+1)*l<n; i++)
            {
                int p1=i*l,p2=(i+1)*l;
                int k=RMQ(rank[p1],rank[p2]);
                int r=l-k%l;
                int p3=p1-r;
                if (p3>=0 && k%l && RMQ(rank[p3],rank[p3+l])>=k) k+=r;
                int c=k/l+1;
                ans=max(ans,c);
            }
        }
        cout<<ans<<endl;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值