poj2774

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

分析:一道后缀数组的题,构造后缀数组有两种算法,dc3和倍增,效率分别为O(n)和O(nlogn),但前者的实现较困难。后缀数组构造后获得了3个数组,sa[i]表示所有后缀排序后的排在第i位的后缀,rank[i]表示原串从第i位到结尾的那个后缀在sa中的排名是多少。height[i]表示sa[i]和sa[i + 1]所表示的字符串的最长公共前缀的长度。本题可以利用height来做,先把两个串连接,找最大height。但是记得在把两个字符串连接时加一个特殊字符作分隔。并注意判断两个sa起始位置不在一个串的内部。之所以要加这个分隔符,首先是可以保证我们所求的两个串的起始位置在sa中是相邻的,如果不加入这个分隔符,答案中两个子串在sa中可能中间会出现一些横跨两个字符串的后缀,遇到这种不相邻的情况,需要用height在两串之间的最小值外加复杂的两串是否合法匹配的判断,例如:aaba,abaaba。如果没有判断好可能会出现abaabaaba与abaaba的匹配。而且这样一来求区间内height最小值的复杂度至少是O(nlogn)(用rmq)。

View Code
#include <iostream>
#include
<cstdlib>
#include
<cstring>
#include
<cstdio>
usingnamespace std;

#define N 200000

char s[N]; // N > 256
int n, sa[N], height[N], rank[N], tmp[N], top[N];
void makesa()
{
// O(N * log N)
int i, j, len, na;
na
= (n <256?256 : n);
memset(top,
0, na *sizeof(int));
for (i =0; i < n; i++)
top[rank[i]
= s[i] &0xff]++;
for (i =1; i < na; i++)
top[i]
+= top[i -1];
for (i =0; i < n; i++)
sa[
--top[rank[i]]] = i;
for (len =1; len < n; len <<=1)
{
for (i =0; i < n; i++)
{
j
= sa[i] - len;
if (j <0)
j
+= n;
tmp[top[rank[j]]
++] = j;
}
sa[tmp[top[
0] =0]] = j =0;
for (i =1; i < n; i++)
{
if (rank[tmp[i]] != rank[tmp[i -1]] || rank[tmp[i] + len] != rank[tmp[i -1] + len])
top[
++j] = i;
sa[tmp[i]]
= j;
}
memcpy(rank, sa, n
*sizeof(int));
memcpy(sa, tmp, n
*sizeof(int));
if (j >= n -1)
break;
}
}
void lcp()
{
// O(4 * N)
int i, j, k;
for (j = rank[height[i = k =0] =0]; i < n -1; i++, k++)
while (k >=0&& s[i] != s[sa[j -1] + k])
height[j]
= (k--), j = rank[sa[j] +1];
}
int main()
{
// freopen("D:\\t.txt", "r", stdin);
gets(s);
int l1 = strlen(s);
s[l1]
='$';
gets(s
+ l1 +1);
int len;
n
= len = strlen(s);
n
++;
s[n]
='$';
makesa();
lcp();
int ans =0;
for (int i =0; i < n; i++)
if ((sa[i] < l1 && sa[i -1] > l1) || (sa[i] > l1 && sa[i -1] < l1))
if (height[i] > ans)
ans
= height[i];
printf(
"%d\n", ans);
return0;
}
posted @ 2011-05-09 11:40  金海峰  阅读(1257)  评论(1编辑  收藏  举报