POJ 3080 Blue Jeans 后缀数组, 高度数组 难度:1

题目

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


题意

有m个(2<=m<=10)不包含空格的字符串,长度为60个字符,求所有字符串中都出现过的最长公共子序列,若该子序列长度小于3,输出"no significant commonalities",否则,输出字典序最小且长度最大的公共子序列。

思路

由于数据较小,其实可以使用比较暴力的思路,但这里为了复习后缀数组采用后缀数组的方法。

1. 将多个字符串合为一个目标字符串buff,为了防止程序无法分清字符串连接处,每个连接处添加一个空格,形如AAAA GGGG。

2. 建立后缀数组,这里采用基数排序的方法。

   a. 令rk数组记录后缀的字典序排名,也即rk[i]为从第i个字符开始的后缀的字典序排名,排名从0开始。

   b. 令sa数组记录排序后的后缀,也即sa[i]为字典序第i的后缀的第一个字符的位置,例如buff='abba',则sa[0]=4代表空串,sa[1]=3代表a,sa[2]=0代表abba,依次类推。

   c. 进行基数排序的初始排序,排序第一个字符。

   d. 初始化跨度k为1,每次完成下面的循环,k乘以2倍,也即每次通过目前的rk数组把已经排序的部分长度扩展2倍。

   e. 用基数排序对后缀[k,2k]进行排序。

         解释:此时后缀[0,k]有序,也即前k个字符一定是按照字母序排序的,举例来说,排序可能是这样,k=2:空串,a,aadd,aacc,abaa,abcc。

                 此时后面k个字符,也即[k,2k]部分是无序的,但是[k,2k]部分的顺序可以和[0,k]部分一一对应起来,因为[k,2k]部分如果不是空串,那么一定是另一个后缀的[0,k]部分。

                 这一步根据这种关系将后缀数组sa排列为[k,2k]为主键,[0,k]为第二位键字典序递增顺序。

   f. 用基数排序对后缀[0,k]进行排序。

         解释:由于上一步排列的顺序,根据基数排序的特点,后缀数组将会变为[0.k]为主键,[k,2k]为第二位键字典序递增顺序,也即[0,2k]遵从字典序排列。

   g. 根据sa计算rk,此处若两个后缀[0,2k]部分相同,则对应rk也应当相同。

   h. 循环直至k >= len,此时明显全部遵从字典序排列。

   i. 根据sa计算rk,此处rk[sa[i]] = i,此时rk的含义将发生改变,变为sa的索引,注意此处若两个后缀[0,2k]相同,其对应rk不相同。

3. 建立高度数组h。(对于从i开始的后缀,如果其高度(即该后缀和在字典序上最接近它且比它字典序小的那个后缀的公共前缀长度)为height,那么i+1开始的后缀高度至少为height-1,利用这一性质,以字符串原有的顺序遍历后缀并且计算其高度。)

4. 得出答案,由题意,设答案的长度为ans,第一个字符是目标串的第start个字符,则h数组中一定有一段连续的子序列都大于等于ans,且对应的后缀分别来自目标字符串的m段。

    a. 建立优先队列,用于存储后缀的高度和index,并获知在当前扫描的这一段中出现过的最小高度。

    b. 用cnt数组监视当前扫描的这一段中每段有多少个后缀出现。可以通过cnt数组获知被扫描的这一段中目标段中有多少段的后缀出现过,设这个值为num。

    c. 设起点为s,若目标串长度为len,则s从0循环至len,对每次循环:

          i. 初始化e=s+1,e不断向后扫,h[s,e]即为当前被扫描的段,直到num==m。

          ii.将最小高度与目前存储的ans进行比较,若最小高度较大,则ans赋值为最小高度,start赋值为s。

               解释:由于rk和h是对应的,所以start被赋予的会是字典序最小长度最长的公共子序列的rk,可以通过sa输出答案。

感想

这道题卡在三个点上:1. 基数排序,先对后半段排序,再对前半段排序。

2. 基数排序,因为在排序过程中将len加入排序,导致边界出错。

3. 基数排序,因为rk是对应于目标串的位置的,所以先对后半段排序时不用改变rk。

代码

  1 #include <cstdio>
  2 #include <map>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <cmath>
  6 #include <string>
  7 #include <vector>
  8 #include <iostream>
  9 #include <assert.h>
 10 #include <sstream>
 11 #include <cctype>
 12 #include <queue>
 13 #include <stack>
 14 #include <map>
 15 #include <iterator>
 16 using namespace std;
 17 typedef long long ll;
 18 typedef pair<int,int> P;
 19 typedef unsigned long long ull;
 20 typedef long long ll;
 21 typedef long double ld;
 22 
 23 const int basenum = 60;
 24 const int basenump1 = basenum + 1;
 25 const int maxm = 10;
 26 const int maxn = basenump1 * maxm + basenump1;
 27 
 28 char buff[maxn];
 29 
 30 int m;
 31 int len;
 32 
 33 int rk[maxn];
 34 int trk[maxn];
 35 int sa[maxn];
 36 int tsa[maxn];
 37 int cnt[maxn];
 38 int h[maxn];
 39 
 40 void debugrk(int *sa, int l){
 41     for(int i = 0;i <= len;i++){
 42         printf("rk[%d]:%d ",i,rk[i]);
 43         for(int j = i;j < len && j < i + l;j++){
 44             putchar(buff[j]);
 45         }
 46         puts("");
 47     }
 48     puts("");
 49 }
 50 
 51 void debugsa(int *sa, int l){
 52     for(int i = 0;i <= len;i++){
 53         printf("sa[%d]:%d rk %d ",i,sa[i], rk[sa[i]]);
 54         for(int j = sa[i];j < len && j < sa[i] + l;j++){
 55             putchar(buff[j]);
 56         }
 57         puts("");
 58     }
 59     puts("");
 60 }
 61 
 62 void debugh(){
 63     for(int i = 0;i <= len;i++){
 64         printf("POS: %d h[%d]:%d ", sa[i],i,h[i]);
 65         for(int j = sa[i];j < len;j++){
 66             putchar(buff[j]);
 67         }
 68         puts("");
 69     }
 70     puts("");
 71 }
 72 
 73 void input()
 74 {
 75     scanf("%d",&m);
 76     len = m * basenump1 - 1;
 77     for(int i = 0; i < m; i++)
 78     {
 79         scanf("%s", buff + i * basenump1);
 80         if(i + 1 < m)buff[i * basenump1 + basenum] = ' ';
 81     }
 82 }
 83 
 84 void build()
 85 {
 86     memset(rk, 0, sizeof rk);
 87     memset(cnt, 0, sizeof cnt);
 88     rk[len] = trk[len] = 0;
 89     sa[0] = tsa[0] = len;
 90     for(int i = 0; i < len; i++)
 91     {
 92         rk[i] = buff[i];
 93         cnt[rk[i]]++;
 94     }
 95     // first radix sort
 96     for(int i = 1; i < 255; i++)
 97     {
 98         cnt[i] += cnt[i - 1];
 99     }
100     for(int i = len - 1; i >= 0; i--)
101     {
102         sa[cnt[rk[i]]--] = i;
103     }
104     for(int k = 1; k < len; k <<= 1)
105     {
106         memset(cnt, 0, sizeof cnt);
107         for(int i = 0; i < len; i++)
108         {
109             cnt[(i + k < len)?rk[i + k]:0]++;
110         }
111         /*
112         Or you can use
113         for(int i = len;i > 0;i--){
114             cnt[(sa[i] + k < len)?rk[sa[i] + k]:0]++;
115         }
116         */
117         for(int i = 1; i <= len; i++)
118         {
119             cnt[i] += cnt[i - 1];
120         }
121         for(int i = len; i > 0; i--)
122         {
123             int ind = cnt[(sa[i] + k < len)?rk[sa[i] + k]:0]--;
124             tsa[ind] = sa[i];
125         }
126         memset(cnt, 0, sizeof cnt);
127         for(int i = 0; i < len; i++)
128         {
129             cnt[rk[i]]++;
130         }
131         /*
132         Or you can use
133         for(int i = len;i > 0;i--){
134             cnt[rk[sa[i]]]++;
135         }
136         */
137         for(int i = 1; i <= len; i++)
138         {
139             cnt[i] += cnt[i - 1];
140         }
141         for(int i = len; i > 0; i--)
142         {
143             int ind = cnt[rk[tsa[i]]]--;
144             sa[ind] = tsa[i];
145         }
146         for(int i = 1, now = sa[i], former = sa[i - 1]; i <= len; i++, now = sa[i], former = sa[i - 1])
147         {
148             trk[now] = trk[former];
149             int nownxt = min(now + k, len);
150             int formernxt = min(former + k, len);
151             if(rk[now] != rk[former] || rk[nownxt] != rk[formernxt]){
152 
153                 trk[now]++;
154             }
155         }
156         for(int i = 0; i < len; i++)
157         {
158             rk[i] = trk[i];
159         }
160     }
161     for(int i = 0;i <= len;i++){
162         rk[sa[i]] = i;
163     }
164     memset(h, 0, sizeof h);
165     //debugsa(sa, len);
166     //debugrk(sa, len);
167     for(int i = 0, height = 0; i < len; i++)
168     {
169         if(height)height--;
170         int former = sa[rk[i] - 1];
171         while(i + height < len && former + height < len && buff[i + height] == buff[former + height])height++;
172         h[rk[i]] = height;
173     }
174     //debugh();
175 }
176 
177 void solve()
178 {
179     build();
180     memset(cnt, 0, sizeof cnt);
181     priority_queue<P, vector<P>, greater<P> >que;
182     int num = 0;
183     int ans = 0;
184     int start = 0;
185     for(int s = 1,e = 1; s <= len; s++)
186     {
187         while(num < m && e <= len)
188         {
189             que.push(P(h[e], e));
190             int ind = sa[e] / basenump1;
191             if(cnt[ind] == 0)num++;
192             cnt[ind]++;
193             e++;
194         }
195         while(que.top().second < s){
196             que.pop();
197         }
198         if(num == m && ans < que.top().first)
199         {
200             ans = que.top().first;
201             start = s - 1;
202         }
203         if(s > 1){
204             int ind = sa[s - 1] / basenump1;
205             if(cnt[ind] == 1)num--;
206             cnt[ind]--;
207         }
208     }
209     if(ans < 3)puts("no significant commonalities");
210     else
211     {
212         for(int i = 0; i < ans; i++)
213         {
214             putchar(buff[sa[start] + i]);
215         }
216         putchar('\n');
217     }
218 }
219 
220 int main()
221 {
222     freopen("input.txt", "r", stdin);
223     int t;
224     scanf("%d", &t);
225     for(int ti = 0; ti < t; ti++)
226     {
227         input();
228         solve();
229     }
230     return 0;
231 }
View Code

 

posted @ 2017-02-07 02:25  雪溯  阅读(287)  评论(0编辑  收藏  举报