POJ 3693 (后缀数组) Maximum repetition substring

找重复次数最多的字串,如果有多解,要求字典序最小。

我也是跟着罗穗骞菊苣的论文才刷这道题的。

首先还是枚举一个循环节的长度L,如果它出现两次的话,一定会包含s[0], s[L], s[2L]这些相邻两个之间。

然后枚举相邻的两个,尽可能的向前和向后延伸,假设延伸长度为k,则重复次数为k / L + 1

向后延伸很自然的就是求一次LCP,这个用RMQ预处理一下就可以O(1)查询。

向前延伸就是考虑到,我们枚举的s[i*L]和s[(i+1)*L]并不一定是字串的开头,所以向前移动i - (k % i)个位置。

为什么向前移动这么多,就是因为这样的话,LCP的长度就正好是i的整数倍了。

所以向前移动以后,再求一次LCP,看看能否够i - (k % i)这么多,够的话这个串的重复次数再加1.

 

至于要字典序最小,就得用height数组天生自带字典序。把所有重复次数最多的长度记录下来,然后按字典序枚举,只要找到一组就输出。

  1 #include <cstdio>

  2 #include <cstring>

  3 #include <algorithm>

  4 using namespace std;

  5 

  6 const int maxn = 100000 + 10;

  7 char s[maxn];

  8 int n;

  9 int sa[maxn], rank[maxn], height[maxn];

 10 int t[maxn], t2[maxn], c[256];

 11 

 12 void build_sa(int n, int m)

 13 {

 14     int i, *x = t, *y = t2;

 15     for(i = 0; i < m; i++) c[i] = 0;

 16     for(i = 0; i < n; i++) c[x[i] = s[i]]++;

 17     for(i = 1; i < m; i++) c[i] += c[i - 1];

 18     for(i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;

 19     for(int k = 1; k <= n; k <<= 1)

 20     {

 21         int p = 0;

 22         for(i = n - k; i < n; i++) y[p++] = i;

 23         for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k;

 24         for(i = 0; i < m; i++) c[i] = 0;

 25         for(i = 0; i < n; i++) c[x[y[i]]]++;

 26         for(i = 1; i < m; i++) c[i] += c[i - 1];

 27         for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];

 28         swap(x, y);

 29         p = 1; x[sa[0]] = 0;

 30         for(i = 1; i < n; i++)

 31             x[sa[i]] = y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? p - 1 : p++;

 32         if(p >= n) break;

 33         m = p;

 34     }

 35 }

 36 

 37 void build_height()

 38 {

 39     int k = 0;

 40     for(int i = 1; i <= n; i++) rank[sa[i]] = i;

 41     for(int i = 0; i < n; i++)

 42     {

 43         if(k) k--;

 44         int j = sa[rank[i] - 1];

 45         while(s[i + k] == s[j + k]) k++;

 46         height[rank[i]] = k;

 47     }

 48 }

 49 

 50 int d[maxn][20];

 51 

 52 void init_RMQ()

 53 {

 54     for(int i = 0; i < n; i++) d[i][0] = height[i + 1];

 55     for(int j = 1; (1 << j) <= n; j++)

 56         for(int i = 0; i + (1 << j) - 1 < n; i++)

 57             d[i][j] = min(d[i][j-1], d[i + (1<<(j-1))][j-1]);

 58 }

 59 

 60 int RMQ(int L, int R)

 61 {

 62     int k = 0;

 63     while( (1 << (k+1)) <= (R - L + 1) ) k++;

 64     return min(d[L][k], d[R-(1<<k)+1][k]);

 65 }

 66 

 67 int LCP(int i, int j)

 68 {

 69     i = rank[i] - 1; j = rank[j] - 1;

 70     if(i > j) swap(i, j);

 71     return RMQ(i + 1, j);

 72 }

 73 

 74 int a[maxn], cnt, maxl;

 75 

 76 int main()

 77 {

 78     //freopen("in.txt", "r", stdin);

 79 

 80     int kase = 0;

 81     while(scanf("%s", s) == 1 && s[0] != '#')

 82     {

 83         n = strlen(s);

 84         build_sa(n + 1, 256);

 85         build_height();

 86         init_RMQ();

 87 

 88         cnt = maxl = 0;

 89         for(int i = 1; i < n; i++)

 90         {

 91             for(int j = 0; j + i < n; j += i)

 92             {

 93                 int k = LCP(j, j + i);

 94                 int t = k / i + 1;

 95                 int left = i - (k % i);

 96                 int head = j - left;

 97                 if(head >= 0 && LCP(head, head + i) >= left) t++;

 98                 if(t > maxl)

 99                 {

100                     cnt = 0;

101                     a[cnt++] = i;

102                     maxl = t;

103                 }

104                 if(t == maxl) a[cnt++] = i;

105             }

106         }

107 

108         int len = -1, st;

109         for(int i = 1; i <= n && len == -1; i++)

110         {

111             for(int j = 0; j < cnt; j++)

112             {

113                 int l = a[j];

114                 if(LCP(sa[i], sa[i] + l) >= (maxl - 1) * l)

115                 {

116                     len = l;

117                     st = sa[i];

118                     break;

119                 }

120             }

121         }

122 

123         printf("Case %d: ", ++kase);

124         for(int i = 0; i < len * maxl; i++) printf("%c", s[st + i]);

125         puts("");

126     }

127 

128     return 0;

129 }
代码君

 

你可能感兴趣的:(substring)