POJ 3693 Maximum repetition substring(连续重复子串)

http://poj.org/problem?id=3693

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

思路:

这道题确实是搞了很久,首先枚举连续子串的长度L,那么子串肯定包含了r[k],r[k+2*L],r[k+3*L].....(k是某个数)中相邻的两个。现在我们只需要枚举这相邻的两个,求出它们的最长公共前缀M,那么重复次数就是M/L+1。

由于要求的是字典序最小,最后再用sa数组从最前面的子串去找即可,符合条件的第一个即是答案。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<vector>
  6 #include<stack>
  7 #include<queue>
  8 #include<cmath>
  9 #include<map>
 10 #include<set>
 11 using namespace std;
 12 typedef long long ll;
 13 typedef pair<int,int> pll;
 14 const int INF = 0x3f3f3f3f;
 15 const int maxn=200000+5;
 16 
 17 int n;
 18 char s[maxn];
 19 int sa[maxn],t[maxn],t2[maxn],c[maxn];
 20 int Rank[maxn],height[maxn];
 21 int d[maxn][30];
 22 int ans[maxn];
 23 
 24 void build_sa(int m)
 25 {
 26     int *x=t,*y=t2;
 27     //基数排序
 28     for(int i=0;i<m;i++)    c[i]=0;
 29     for(int i=0;i<n;i++)    c[x[i]=s[i]]++;
 30     for(int i=1;i<m;i++)    c[i]+=c[i-1];
 31     for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
 32     for(int k=1;k<=n;k<<=1)
 33     {
 34         int p=0;
 35         //直接利用sa数组排序第二关键字
 36         for(int i=n-k;i<n;i++)  y[p++]=i;
 37         for(int i=0;i<n;i++)    if(sa[i]>=k)    y[p++]=sa[i]-k;
 38         //基数排序第一关键字
 39         for(int i=0;i<m;i++)    c[i]=0;
 40         for(int i=0;i<n;i++)    c[x[y[i]]]++;
 41         for(int i=1;i<m;i++)    c[i]+=c[i-1];
 42         for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
 43         //根据sa和y计算新的x数组
 44         swap(x,y);
 45         p=1;
 46         x[sa[0]]=0;
 47         for(int i=1;i<n;i++)
 48             x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
 49         if(p>=n)
 50             break;
 51         m=p;                //下次基数排序的最大值
 52     }
 53 }
 54 
 55 void getHeight(int n)
 56 {
 57     int i,j,k=0;
 58     for(i=1;i<=n;i++)  Rank[sa[i]]=i;
 59     for(i=0;i<n;i++)
 60     {
 61         if(k)  k--;
 62         int j=sa[Rank[i]-1];
 63         while(s[i+k]==s[j+k])  k++;
 64         height[Rank[i]]=k;
 65     }
 66 }
 67 
 68 void RMQ(int n)
 69 {
 70     for(int i=1;i<=n;i++)  d[i-1][0]=height[i];
 71     for(int j=1;(1<<j)<=n;j++)
 72         for(int i=0;i+(1<<j)-1<n;i++)
 73         d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
 74 }
 75 
 76 int query(int L, int R)
 77 {
 78     int k=0;
 79     while((1<<(k+1))<=R-L+1)  k++;
 80     return min(d[L][k],d[R-(1<<k)+1][k]);
 81 }
 82 
 83 int LCP(int a, int b)
 84 {
 85     int x=Rank[a],y=Rank[b];
 86     if(x>y)  swap(x,y);
 87     x--; y--;
 88     if(y<0)  return 0;
 89     return query(x+1,y);
 90 }
 91 
 92 void solve(int n)
 93 {
 94     int MAX=-1;
 95     int len = 0;
 96     for(int l=1;l<n;l++)   //枚举子串长度
 97     {
 98         for(int i=0;i+l<n;i+=l)  //枚举起点
 99         {
100             int k=LCP(i,i+l);
101             int m=k/l+1;
102             int t=l-k%l;  //如果不是l的倍数,则往前几位再匹配,往后匹配已经匹配不上了
103             t=i-t;
104             if(t>=0 && k%l)
105             {
106                 if(LCP(t,t+l)>=k)  m++;
107             }
108             if(m>MAX)
109             {
110                 len=0;
111                 ans[len++]=l;
112                 MAX=m;
113             }
114             else if(m==MAX)
115                 ans[len++]=l;
116         }
117     }
118     int l, start;  //寻找字典序最下的答案
119     bool flag=false;
120     for(int i=1;i<=n;i++)
121     {
122         if(flag)  break;
123         for(int j=0;j<len;j++)
124         {
125             int tmp=ans[j];
126             if(LCP(sa[i],sa[i]+tmp)>=(MAX-1)*tmp)
127             {
128                 start=sa[i];
129                 l=tmp*MAX;
130                 flag=true;
131                 break;
132             }
133         }
134     }
135     for(int i=start;i<start+l;i++)
136         printf("%c",s[i]);
137 
138     printf("
");
139 }
140 
141 int main()
142 {
143     //freopen("in.txt","r",stdin);
144     int kase=0;
145     while(~scanf("%s",s))
146     {
147         if(s[0]=='#')  break;
148         printf("Case %d: ",++kase);
149         n=strlen(s);
150         if(n==1)  {printf("%c
",s[0]);continue;}
151         n=strlen(s);
152         s[n]='0';
153         s[n+1]='';
154         n=strlen(s);
155         n++;
156         build_sa(128);
157         getHeight(n-1);
158         RMQ(n-1);
159         solve(n-1);
160     }
161     return 0;
162 }
原文地址:https://www.cnblogs.com/zyb993963526/p/7623179.html