poj 2774 后缀数组(模板题)

来源:互联网 发布:打开db数据库 编辑:程序博客网 时间:2024/06/14 16:48

点击打开链接

模板

#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>using namespace std;const int M =1e6+20;int len,len1,len2;char s[M];int sa[M];//sa[i]排名i的后缀编号 int c[M];//计数数组 c[i]=j表示关键字<=i的有j个  int t1[M],t2[M]; void SA(char *s,int n,int m)//n为strlen(s)+1; m为最大字符的ascii码值{int *x=t1;//x[i]=j表示后缀i的排名 (每一轮的rank) int *y=t2;//y[p]=i;后缀i的第二关键字排名为p int i;for(i=0;i<m;i++)c[i]=0;for(i=0;i<n;i++){c[x[i]=s[i]]++;//长度为1时 x[i]的排名就为该字符ascii值}for(i=1;i<m;i++)c[i]+=c[i-1];for(i=n-1;i>=0;i--)//长度(1)的名次数组 sa[--c[x[i]]]=i;//aabaaaab 若x[i]值相同的sa靠前的小 for(int k=1;k<=n;k<<=1){int p=0;for(i=n-k;i<n;i++)y[p++]=i;//第二关键字为0的排前面 for(i=0;i<n;i++){//倍增:x+k后缀的第一关键字排名等于x后缀的第二关键字排名 if(sa[i]>=k)//只有编号大于k的才能作为第二关键字 {y[p++]=sa[i]-k;}}//通过x[],y[]计算每个后缀的长为2*k前缀的sa数组for(i=0;i<m;i++)c[i]=0;for(i=0;i<n;i++)c[x[y[i]]]++;//第二关键字排名为i的后缀y[i]的第一关键字排名//此时c[p]的值表示第一关键字排名为p的有多少个 for(i=1;i<m;i++)c[i]+=c[i-1];// 此时c[i]:第一关键字的排名为i时,小于等于它的个数 for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];//后缀y[i]在它的2*k长度中排名为? //后缀y[i]的第一关键字排名为c[x[y[i]]] 第一关键字相同时 此时看第二关键字//y[i]中i越小 第二关键字排名就越小 后缀y[i]排名就越小 (逆推的原因)//接着更新x为长度2*k的名次数字 swap(x,y);//令y表示名次数组(rank)p=1;//此时p记录排名的不同的个数x[sa[0]]=0;for(i=1;i<n;i++){x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;//两个关键字相同则在x数组的排名是一样的 //y[sa[i]]==y[sa[i-1]],即这两个后缀的长k的第一关键字相同的情况下              //他们必定还存在第二关键需要比较  }if(p>=n) break;//字典序比较法得出 当前排名都不同时,最终排名就为此排名 m=p; //} }int rank[M],height[M];//设 height[i]=LCP(Suffix(sa[i]),Suffix(sa[i-1]))后缀排名为i和i-1的LCP //则 h[i]=height[rank[i]]= 后缀i和它前一个后缀的 LCP//h[i]>=h[i-1]-1//h[i-1]为后缀i-1和它排在它前一个的后缀k的LCP//同时去掉相同的首字符之后 后缀k+1和和后缀i的LCP为h[i]=h[i-1]-1 //因为后缀k+1和后缀i名次不一定相邻 所以若存在后缀t为后缀i相邻 则h[i]>=h[i]-1; void Height(){int i,j,k=0;for(i=0;i<len;i++)rank[sa[i]]=i;for(i=0;i<len;i++){if(k)k--;//h[i]>=h[i-1]-1;先减一int j=sa[rank[i]-1];//前一个后缀 while(s[i+k]==s[j+k])k++;height[rank[i]]=k;}} void solve(){int ans=0;for(int i=2;i<len;i++){if(height[i]>ans){//后缀sa[i]和sa[i-1]不能在同一个串中 if(sa[i-1]>=0&&sa[i-1]<len1&&sa[i]>len1&&sa[i]<len){ans=height[i];}if(sa[i]>=0&&sa[i]<len1&&sa[i-1]>len1&&sa[i-1]<len){ans=height[i];}}}printf("%d\n",ans);}int main(){scanf("%s",s);len1=strlen(s);s[len1]=1;//分隔符 scanf("%s",s+len1+1);//拼接两个粗串len2=strlen(s)-len1-1;s[len1+len2+1]=0;//末尾 len=len1+len2+2;SA(s,len,128);Height();solve();//求两个串的最长公共字串  //首先如果这两个长字符串存在某个最长的公共子串,那么该子串一定分别是这两个串的后缀的前缀. //将两个串拼接起来求最大的LCP&&两个后缀不属于同一个串 return 0;}



0 0
原创粉丝点击