码迷,mamicode.com
首页 > 编程语言 > 详细

[POJ1226]Substrings(后缀数组)

时间:2017-06-13 09:57:41      阅读:216      评论:0      收藏:0      [点我收藏+]

标签:分组   难度   line   turn   opened   cstring   ret   log   return   

传送门

 

给定 n 个字符串,求出现或反转后出现在每个字符串中的最长子串。

算法分析:

这题不同的地方在于要判断是否在反转后的字符串中出现。其实这并没有加大题目的难度。

只需要先将每个字符串都反过来写一遍,中间用一个互不相同的且没有出现在字符串中的字符隔开,

再将 n 个字符串全部连起来,中间也是用一个互不相同的且没有出现在字符串中的字符隔开,求后缀数组。

然后二分答案,再将后缀分组。

判断的时候,要看是否有一组后缀在每个原来的字符串或反转后的字符串中出现。

这个做法的时间复杂度为 O(nlogn)。

 

——代码

技术分享
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #define N 21001
  5 
  6 int len, n, m, max_num, T;
  7 int buc[N], x[N], y[N], sa[N], rank[N], height[N], belong[N], s[N];
  8 char a[N];
  9 bool f[101];
 10 
 11 inline void build_sa()
 12 {
 13     int i, k, p;
 14     for(i = 0; i < m; i++) buc[i] = 0;
 15     for(i = 0; i < len; i++) buc[x[i] = s[i]]++;
 16     for(i = 1; i < m; i++) buc[i] += buc[i - 1];
 17     for(i = len - 1; i >= 0; i--) sa[--buc[x[i]]] = i;
 18     for(k = 1; k <= len; k <<= 1)
 19     {
 20         p = 0;
 21         for(i = len - 1; i >= len - k; i--) y[p++] = i;
 22         for(i = 0; i < len; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
 23         for(i = 0; i < m; i++) buc[i] = 0;
 24         for(i = 0; i < len; i++) buc[x[y[i]]]++;
 25         for(i = 1; i < m; i++) buc[i] += buc[i - 1];
 26         for(i = len - 1; i >= 0; i--) sa[--buc[x[y[i]]]] = y[i];
 27         std::swap(x, y);
 28         p = 1, x[sa[0]] = 0;
 29         for(i = 1; i < len; i++)
 30             x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
 31         if(p >= len) break;
 32         m = p;
 33     }
 34 }
 35 
 36 inline void build_height()
 37 {
 38     int i, j, k = 0;
 39     for(i = 0; i < len; i++) rank[sa[i]] = i;
 40     for(i = 0; i < len; i++)
 41     {
 42         if(!rank[i]) continue;
 43         if(k) k--;
 44         j = sa[rank[i] - 1];
 45         while(s[i + k] == s[j + k] && i + k < len && j + k < len) k++;
 46         height[rank[i]] = k;
 47     }
 48 }
 49 
 50 inline bool check(int k)
 51 {
 52     int i, cnt = 1;
 53     memset(f, 0, sizeof(f));
 54     f[belong[sa[0]]] = 1;
 55     for(i = 1; i < len; i++)
 56         if(height[i] >= k && !f[belong[sa[i]]])
 57         {
 58             cnt++;
 59             f[belong[sa[i]]] = 1;
 60             if(cnt == n) return 1;
 61         }
 62         else if(height[i] < k)
 63         {
 64             cnt = 1;
 65             memset(f, 0, sizeof(f));
 66             f[belong[sa[i]]] = 1;
 67         }
 68     return 0;
 69 }
 70 
 71 inline int solve()
 72 {
 73     int l = 1, r = len, ans = 0, mid;
 74     while(l <= r)
 75     {
 76         mid = (l + r) >> 1;
 77         if(check(mid)) ans = mid, l = mid + 1;
 78         else r = mid - 1;
 79     }
 80     return ans;
 81 }
 82 
 83 int main()
 84 {
 85     int i, j, l;
 86     scanf("%d", &T);
 87     while(T--)
 88     {
 89         len = 0;
 90         m = 400;
 91         scanf("%d", &n);
 92         for(i = 0; i < n; i++)
 93         {
 94             scanf("%s", a);
 95             l = strlen(a);
 96             for(j = 0; j < l; j++) belong[len] = i, s[len++] = a[j];
 97             belong[len] = i;
 98             s[len++] = 130 + (i << 1);
 99             for(j = l - 1; j >= 0; j--) belong[len] = i, s[len++] = a[j];
100             belong[len] = i;
101             s[len++] = 130 + (i << 1 | 1);
102         }
103         len--;
104         build_sa();
105         build_height();
106         if(n == 1)
107         {
108             printf("%d\n", l);
109             continue;
110         }
111         printf("%d\n", solve());
112     }
113     return 0;
114 }
View Code

 

[POJ1226]Substrings(后缀数组)

标签:分组   难度   line   turn   opened   cstring   ret   log   return   

原文地址:http://www.cnblogs.com/zhenghaotian/p/6999266.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!