hdu2328(后缀数组 + 二分)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2328

 

题意: 求 n 个串的字典序最小的最长公共子串

 

思路: 本题中单个字符串长度不超过 200, 可以暴力枚举一个字符串的所有前缀, 然后用kmp去匹配其他字符串.

我这里是用后缀数组写的. 类似 http://www.cnblogs.com/geloutingyu/p/7450580.html

不过本题是有 n 个字符串, 不能直接暴力判断. 不难想到这里可以直接二分答案长度, 不过 check 函数比较难想到, 具体看代码

 

代码:

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #define rank Rank
  5 using namespace std;
  6 
  7 const int MAXN = 1e6 + 10;
  8 int sol;
  9 char str[MAXN];
 10 int SA[MAXN], rank[MAXN], height[MAXN], sum[MAXN], tp[MAXN], vis[MAXN], tag[(int)(4e3 + 10)];
 11 
 12 bool cmp(int *f, int x, int y, int w){
 13     return f[x] == f[y] && f[x + w] == f[y + w];
 14 }
 15 
 16 void DA(char *s, int n, int m){
 17     for(int i = 0; i < m; i++) sum[i] = 0;
 18     for(int i = 0; i < n; i++) sum[rank[i] = s[i]]++;
 19     for(int i = 1; i < m; i++) sum[i] += sum[i - 1];
 20     for(int i = n - 1; i >= 0; i--) SA[--sum[rank[i]]] = i;
 21     for(int len = 1; len <= n; len <<= 1){
 22         int p = 0;
 23         for(int i = n - len; i < n; i++) tp[p++] = i;
 24         for(int i = 0; i < n; i++){
 25             if(SA[i] >= len) tp[p++] = SA[i] - len;
 26         }
 27         for(int i = 0; i < m; i++) sum[i] = 0;
 28         for(int i = 0; i < n; i++) sum[rank[tp[i]]]++;
 29         for(int i = 1; i < m; i++) sum[i] += sum[i - 1];
 30         for(int i = n - 1; i >= 0; i--) SA[--sum[rank[tp[i]]]] = tp[i];
 31         swap(rank, tp);
 32         p = 1;
 33         rank[SA[0]] = 0;
 34         for(int i = 1; i < n; i++){
 35             rank[SA[i]] = cmp(tp, SA[i - 1], SA[i], len) ? p - 1 : p++;
 36         }
 37         if(p >= n) break;
 38         m = p;
 39     }
 40     int k = 0;
 41     n--;
 42     for(int i = 0; i <= n; i++) rank[SA[i]] = i;
 43     for(int i = 0; i < n; i++){
 44         if(k) k--;
 45         int j = SA[rank[i] - 1];
 46         while(s[i + k] == s[j + k]) k++;
 47         height[rank[i]] = k;
 48     }
 49 }
 50 
 51 bool check(int mid, int n, int len){
 52     int cnt = 1;
 53     memset(tag, 0, sizeof(tag));
 54     tag[vis[SA[1]]] = 1;
 55     for(int i = 2; i <= len; i++){
 56         if(height[i] >= mid){
 57             if(!tag[vis[SA[i]]]){
 58                 cnt++;
 59                 tag[vis[SA[i]]] = 1;
 60                 if(cnt >= n){
 61                     sol = SA[i];
 62                     return true;
 63                 }
 64             }
 65         }else{
 66             cnt = 1;
 67             memset(tag, 0, sizeof(tag));
 68             tag[vis[SA[i]]] = 1;
 69         }
 70     }
 71     return false;
 72 }
 73 
 74 int main(void){
 75     int n;
 76     while(~scanf("%d", &n) && n){
 77         memset(vis, 0, sizeof(vis));
 78         scanf("%s", str);
 79         int len = strlen(str);
 80         int mx = len;
 81         for(int i = 1; i < n; i++){
 82             vis[len] = 1;
 83             str[len] = '0';
 84             scanf("%s", str + 1 + len);
 85             mx = max(mx, (int)(strlen(str) - len));
 86             len = strlen(str);
 87         }
 88         str[len] = 0;
 89         DA(str, len + 1, 'z' + 1);
 90         for(int i = 1; i <= len; i++) vis[i] += vis[i - 1];
 91         int l = 1, r = mx;
 92         while(l <= r){
 93             int mid = (l + r) >> 1;
 94             if(check(mid, n, len)) l = mid + 1;
 95             else r = mid - 1;
 96         }
 97         if(l - 1 <= 0){
 98             puts("IDENTITY LOST");
 99             continue;
100         }
101         for(int i = sol, j = 1; j <= l - 1; i++, j++){
102             printf("%c", str[i]);
103         }
104         puts("");
105     }
106     return 0;
107 }
View Code

 

posted @ 2017-09-02 19:07  geloutingyu  阅读(323)  评论(0编辑  收藏  举报