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

来源:互联网 发布:淘宝万斯正品店 编辑:程序博客网 时间:2024/05/17 04:25

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

      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;    }}


0 0