hdu1403(后缀数组模板)

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

 

题意: 给出两个字符串, 求他们的最长公共子串

 

思路: 两个字符串的最长公共子串长度显然就是两个字符串的所有后缀中的最长公共前缀长度. 可以先用一个没有出现的字符(便于后面区分后缀是否属于相同字符串)将两个字符串连成一个字符串,再用后缀数组求其height, SA数组, 对于当前 i, 通过 SA 数组区分后缀 i 和 i - 1 是否在同一个初始字符串中, 若不是则用 height[i] 维护答案最大值.

 

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <algorithm>
 5 #define rank Rank
 6 using namespace std;
 7 
 8 const int MAXN = 2e5 + 10;
 9 
10 char str[MAXN];
11 int SA[MAXN], rank[MAXN], height[MAXN], sum[MAXN], tp[MAXN];
12 //rank[i] 第i个后缀的排名, SA[i] 排名为i的后缀的位置, Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
13 //sum[i] 基数排序辅助数组, 存储小于i的元素有多少个, tp[i] rank的辅助数组(按第二关键字排序的结果),与SA意义一样
14 
15 bool cmp(int *f, int x, int y, int w){
16     return f[x] == f[y] && f[x + w] == f[y + w];
17 }
18 
19 void get_SA(char *s, int n, int m){
20     //先预处理长度为1的情况
21     for(int i = 0; i < m; i++) sum[i] = 0;//清0
22     for(int i = 0; i < n; i++) sum[rank[i] = s[i]]++;//统计每个字符出现的次数
23     for(int i = 1; i < m; i++) sum[i] += sum[i - 1];//sum[i]为小于等于i的元素的数目
24     for(int i = n - 1; i >= 0; i--) SA[--sum[rank[i]]] = i;//下标从0开始,所以先自减
25     //SA[i]存储排名第i的后缀下标,SA[--sum[rank[i]]] = i 即下标为i的后缀排名为--sum[rank[i]],这很显然
26     for(int len = 1; len <= n; len <<= 1){
27         int p = 0;
28         //直接用SA数组对第二关键字排序
29         for(int i = n - len; i < n; i++) tp[p++] = i;//后面i个数没有第二关键字,即第二关键字为空,所以最小
30         for(int i = 0; i < n; i++){
31             if(SA[i] >= len) tp[p++] = SA[i] - len;
32         }
33         //tp[i]存储按第二关键字排序第i的下标
34         //对第二关键字排序的结果再按第一关键字排序,和长度为1的情况类似
35         for(int i = 0; i < m; i++) sum[i] = 0;
36         for(int i = 0; i < n; i++) sum[rank[tp[i]]]++;
37         for(int i = 1; i < m; i++) sum[i] += sum[i - 1];
38         for(int i = n - 1; i >= 0; i--) SA[--sum[rank[tp[i]]]] = tp[i];
39         //根据SA和rank数组重新计算rank数组
40         swap(rank, tp);//交换后tp指向旧的rank数组
41         p = 1;
42         rank[SA[0]] = 0;
43         for(int i = 1; i < n; i++){
44             rank[SA[i]] = cmp(tp, SA[i - 1], SA[i], len) ? p - 1 : p++;//注意判定rank[i]和rank[i-1]是否相等
45         }
46         if(p >= n) break;
47         m = p;//下次基数排序的最大值
48     }
49     //求height
50     int k = 0;
51     n--;
52     for(int i = 0; i <= n; i++) rank[SA[i]] = i;
53     for(int i = 0; i < n; i++){
54         if(k) k--;
55         int j = SA[rank[i] - 1];
56         while(s[i + k] == s[j + k]) k++;
57         height[rank[i]] = k;
58     }
59 }
60 
61 int main(void){
62     char str[MAXN];
63     while(~scanf("%s", str)){
64         int len = strlen(str);
65         str[len] = '0';
66         scanf("%s", str + len + 1);
67         int n = strlen(str);
68         str[n] = 0; //末尾添加一个0
69         get_SA(str, n + 1, 'z' + 1);
70         int sol = 0;
71         for(int i = 1; i < n; i++){
72             if(SA[i] > len && SA[i - 1] < len) sol = max(sol, height[i]);
73             if(SA[i] < len && SA[i - 1] > len) sol = max(sol, height[i]);
74         }
75         printf("%d\n", sol);
76     }
77     return 0;
78 }
View Code

 

posted @ 2017-08-29 22:23  geloutingyu  阅读(596)  评论(0编辑  收藏  举报