Poj 2774 Long Long Message (后缀数组)

题目链接:

  Poj  2774  Long Long Message

题目描述:

  给出A,B两个字符串,求最长公共子串?

解题思路:

  求A,B字符串的最长公共子串可以转化为求A,B字符串后缀数组的最长公共前缀。把B串连接在A串后面,用'$'隔开组成r串。求出r串的height数组,最大的height[i](满足sa[i]与sa[i-1]不在同一文本串中)就是答案。时间复杂度大概为O((|A|+|B|)*log(|A|+|B|))。(阅兵假期用来搞这个,感觉还是有点怪怪的感觉,不过秉承着以前的学习风格,先记下模板,以后在水题中慢慢体会好了)

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 typedef long long LL;
 7 const int maxn = 200010;
 8 
 9 int sa[maxn], rank[maxn], height[maxn];
10 int t1[maxn], t2[maxn], r[maxn], c[maxn];
11 bool cmp (int *str, int a, int b, int k)
12 {//rank相邻的两个串,第一第二关键字都一样,rank一样
13     return str[a]==str[b] && str[a+k]==str[b+k];
14 }
15 int da (int *str, int n, int m)
16 {
17     int *x = t1, *y = t2, i, j;
18     n ++;
19     //基数排序,
20     for (i=0; i<m; i++) c[i] = 0;
21     for (i=0; i<n; i++) c[x[i] = str[i]] ++;
22     for (i=1; i<m; i++) c[i] += c[i-1];
23     for (i=n-1; i>=0; i--) sa[-- c[str[i]]] = i;
24     for (j=1; j<=n; j*=2)
25     {
26         //用sa数组对第二关键字排序
27         int p = 0;
28         for (i=n-j; i<n; i++) y[p++] = i;  //不能添加长度为j的串,第二关键字附为最小
29         for (i=0; i<n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;
30         //枚举rank,起点大于j的串,才能倍增
31         //更新sa数组
32         for (i=0; i<m; i++) c[i] = 0;
33         for (i=0; i<n; i++) c[x[y[i]]] ++;
34         for (i=1; i<m; i++) c[i] += c[i-1];
35         for (i=n-1; i>=0; i--) sa[-- c[x[y[i]]]] = y[i];
36         //更新x数组
37         swap (x, y);
38         p = 1, x[sa[0]] = 0;
39         for (i=1; i<n; i++)
40             x[sa[i]] = cmp(y, sa[i-1], sa[i], j)?p-1:p++;
41         if (p >= n)
42             break;
43         m = p;
44     }
45     //计算rank数组
46     for (i=0; i<n; i++)
47         rank[sa[i]] = i;
48     //计算height数组
49     n --;
50     int k = 0;
51     for (i=0; i<n; i++)
52     {//枚举起点, height[名次]
53         if (k)  k --;
54         j = sa[rank[i]-1];
55         while (str[i+k]==str[j+k])    k++;
56         height[rank[i]] = k;
57     }
58 }
59 int main ()
60 {
61     char str1[maxn/2], str2[maxn/2];
62     while (scanf ("%s %s", str1, str2) != EOF)
63     {
64         int n1 = strlen(str1);
65         int n2 = strlen(str2);
66         int n = n1 + n2 + 1;
67         for (int i=0; i<n; i++)
68         {
69             if (i < n1)
70                 r[i] = str1[i];
71             else if (i == n1)
72                 r[i] = '$';
73             else
74                 r[i] = str2[i-n1-1];
75         }
76         r[n] = 0;
77         da (r, n, 128);
78         int ans = 0;
79         for (int i=1; i<n; i++)
80         {//i为rank
81             if (sa[i]<n1&&sa[i-1]>n1 || sa[i]>n1&&sa[i-1]<n1)
82                 //rank相邻的后缀串不在同一个字符串
83                 ans = max(ans, height[i]);
84         }
85         printf ("%d\n", ans);
86     }
87     return 0;
88 }

 

posted @ 2015-09-04 17:19  罗茜  阅读(182)  评论(0编辑  收藏  举报