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 }
我就是我,颜色不一样的烟火 --- geloutingyu