[URAL1517 Freedom of Choice]

[关键字]:后缀数组 字符串

[题目大意]:给定两个字符串A 和B,求最长公共子串。

//==============================================================================

[分析]:求两个字符串的最长公共子串实际上就是求两个字符串的两个后缀的最长公共前缀。所以把两个字符串连起来中间用一个没出现的字符(小于任何一个原字符串中的字符)连起来,最后再加上一个字符(小于任何一个字符)。然后求这个新串中的最长公共前缀,依次枚举每个i求出符合sa[i-1]在一串,sa[i]在二串的所有height的最大值。

[代码]:

View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=200010;
const int MAXL=200010;

int n,n1,n2;
int top[MAXN],sa[MAXN],r[MAXN*2],temp[MAXN],height[MAXN];
char s[MAXL],s1[MAXL],s2[MAXL],ch;

void Init()
{
scanf("%d",&n);
scanf("%c",&ch);
gets(s1);
gets(s2);
n1=strlen(s1);
for (int i=0;i<n1;++i)
s[i]=s1[i];
s[n1]='$';
n2=strlen(s2);
for (int i=0;i<n2;++i)
s[n1+1+i]=s2[i];
n=n1+n2+1;
s[n++]='#';
}

void Make()
{
int i,j,len,m;
memset(top,0,sizeof(top));
m=n<256?256:n;
for (i=0;i<n;++i) ++top[r[i]=s[i]&0xff];
for (i=1;i<m;++i) top[i]+=top[i-1];
for (i=0;i<n;++i) sa[--top[r[i]]]=i;
for (len=1;len<n;len<<=1)
{
for (i=0;i<n;++i)
{
j=sa[i]-len;
if (j<0) j+=n;
temp[top[r[j]]++]=j;
}
sa[temp[top[0]=0]]=j=0;
for (i=1;i<n;++i)
{
if (r[temp[i]]!=r[temp[i-1]] ||
r[temp[i]+len]!=r[temp[i-1]+len])
top[++j]=i;
sa[temp[i]]=j;
}
memcpy(r,sa,sizeof(sa));
memcpy(sa,temp,sizeof(sa));
if (j>=n-1) break;
}
}

void Lcp()
{
int i,j,k;
for (j=r[height[i=k=0]=0];i<n-1;++i,++k)
while (k>=0 && s[i]!=s[sa[j-1]+k])
height[j]=k--,j=r[sa[j]+1];
}

void Solve()
{
// for (int i=0;i<=n;++i) printf("%d ",height[i]);
// printf("\n");
int MAX=0,MAXJ;
for (int i=3;i<n;++i)
if ((sa[i]<n1 && sa[i-1]>n1) || (sa[i]>n1 && sa[i-1]<n1))
if (MAX<height[i]) MAX=height[i],MAXJ=sa[i];
//printf("%d\n",MAX);
for (int i=MAXJ;i<MAXJ+MAX;++i) printf("%c",s[i]);
printf("\n");
}

int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
Init();
//printf("%d %s\n",n,s);
Make();
Lcp();
Solve();
return 0;
}



你可能感兴趣的:(free)