首页 > 代码库 > POJ - 3693 Maximum repetition substring 后缀数组+RMQ

POJ - 3693 Maximum repetition substring 后缀数组+RMQ

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

整体思路就是枚举长度L,看长度为L的字符串在s中连续出现了几次。

既然长度为L的串是重复出现的,那么s[0]…s[L]…s[2*L]…中相邻的两个一定出现在重复的L串中。(并不一定在首尾位置,也可能出现在中间)。

那么我们求i*L和(i+1)*L的LCP,假设答案为M,那么答案应该是M/L+1(加一是因为i*L~(i+1)*L也是答案)。

但是这个答案不一定是最优,举一个下面的例子。

技术分享

此时M/L+1是3,而正确答案是4。为什么呢?是因为我们枚举的相邻位置不一定在首尾位置。M%L可以理解为末尾多出的字符,我们更可以理解为是前面少了L-M%L个字符,我们把起始位置挪前,挪到(i*L-(L-M%L))位置,同理,(i+1)*L也挪。

保证字典序最小,我们只要将sa数组从前往后枚举即可。

这题还学会了这样的复杂度的大小技术分享

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cstring>
  5 #include <cmath>
  6 #include <vector>
  7 using namespace std;
  8 const int maxn = 1e5+5;
  9 int t1[maxn],t2[maxn],c[maxn];
 10 bool cmp(int *r,int a,int b,int l)
 11 {
 12     return r[a] == r[b] && r[a+l] == r[b+l];
 13 }
 14 void da(int str[],int sa[],int Rank[],int height[],int n,int m)
 15 {
 16     n++;
 17     int i, j, p, *x = t1, *y = t2;
 18     //第一轮基数排序,如果s的最大值很大,可改为快速排序
 19     for(i = 0;i < m;i++)c[i] = 0;
 20     for(i = 0;i < n;i++)c[x[i] = str[i]]++;
 21     for(i = 1;i < m;i++)c[i] += c[i-1];
 22     for(i = n-1;i >= 0;i--)sa[--c[x[i]]] = i;
 23     for(j = 1;j <= n; j <<= 1)
 24     {
 25         p = 0;
 26         //直接利用sa数组排序第二关键字
 27         for(i = n-j; i < n; i++)y[p++] = i;//后面的j个数第二关键字为空的最小
 28         for(i = 0; i < n; i++)if(sa[i] >= j)y[p++] = sa[i] - j;
 29         //这样数组y保存的就是按照第二关键字排序的结果
 30         //基数排序第一关键字
 31         for(i = 0; i < m; i++)c[i] = 0;
 32         for(i = 0; i < n; i++)c[x[y[i]]]++;
 33         for(i = 1; i < m;i++)c[i] += c[i-1];
 34         for(i = n-1; i >= 0;i--)sa[--c[x[y[i]]]] = y[i];
 35         //根据sa和x数组计算新的x数组
 36         swap(x,y);
 37         p = 1; x[sa[0]] = 0;
 38         for(i = 1;i < n;i++)
 39             x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
 40         if(p >= n)break;
 41         m = p;//下次基数排序的最大值
 42     }
 43     int k = 0;
 44     n--;
 45     for(i = 0;i <= n;i++)Rank[sa[i]] = i;
 46     for(i = 0;i < n;i++)
 47     {
 48         if(k)k--;
 49         j = sa[Rank[i]-1];
 50         while(str[i+k] == str[j+k])k++;
 51         height[Rank[i]] = k;
 52     }
 53 }
 54 int Rank[maxn],height[maxn];
 55 int sa[maxn];
 56 char s[maxn];
 57 int M[maxn][20];
 58 int r[maxn];
 59 void RMQ(int n)
 60 {
 61     for(int i=1;i<=n;i++) M[i][0] = height[i];
 62     for(int j=1;(1<<j)<=n;j++)
 63     {
 64         for(int i=1;(i+(1<<j)-1)<=n;i++)
 65         {
 66             M[i][j] = min(M[i][j-1],M[i+(1<<(j-1))][j-1]);
 67         }
 68     }
 69 }
 70 int ST(int l,int r)
 71 {
 72     int k = 0;
 73     while((1<<(k+1))<=(r-l+1)) k++;
 74     return min(M[l][k],M[r-(1<<k)+1][k]);
 75 }
 76 int lcp(int a,int b)
 77 {
 78     if(a<0||b<0) return 0;
 79     a = Rank[a],b = Rank[b];
 80     if(a>b) swap(a,b);
 81     return ST(a+1,b);
 82 }
 83 vector<int> length;
 84 int main()
 85 {
 86     int kase = 0;
 87     while(scanf("%s",s)&&s[0]!=#)
 88     {
 89         int n = strlen(s);
 90         for(int i=0;i<n;i++) r[i] = s[i];
 91         r[n] = 0;
 92         da(r,sa,Rank,height,n,128);
 93         RMQ(n);
 94         int maxv = 0;
 95         length.clear();
 96         printf("Case %d: ",++kase);
 97         for(int l=1;l<n;l++)
 98         {
 99             for(int i=0;i+l<n;i+=l)
100             {
101                 int m = lcp(i,i+l);
102                 int step = m/l+1;    //这必须先除,以免后面移动位置后,M变小了
103                 int start = i-(l-m%l); //计算挪到的位置
104                 if(start>=0&&m%l>0)
105                 {
106                     if(lcp(start,start+l)>=m)
107                     {
108                         step++;
109                     }
110                 }
111                 if(step>maxv)
112                 {
113                     maxv = step;
114                     length.clear();
115                     length.push_back(l);
116                 }
117                 else if(step==maxv)
118                 {
119                     length.push_back(l);
120                 }
121             }
122         }
123       //  printf("%d\n",maxv);
124         int start = 0;
125         int printlen = 0;
126         int flag = 0;
127         //枚举排名,保证字典序最小
128         for(int i=1;i<=n&&!flag;i++)
129         {
130             int tt = length.size();
131             for(int j=0;j<tt;j++)
132             {
133                 int l = length[j];
134                 if(lcp(sa[i],sa[i]+l)>=(maxv-1)*l)
135                 {
136                     start = sa[i];
137                     printlen = l;
138                     flag = 1;
139                     break;
140                 }
141             }
142         }
143        // printf("%d %d %d\n",start,maxv,printlen);
144         for(int i=start;i<start+maxv*printlen;i++)
145         {
146             printf("%c",s[i]);
147         }
148         printf("\n");
149     }
150     return 0;
151 }

 

POJ - 3693 Maximum repetition substring 后缀数组+RMQ