bzoj 2946: [Poi2000]公共串

Description

 
        给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l         读入单词
l         计算最长公共子串的长度
l         输出结果
 

Input

 
文件的第一行是整数  n 1<=n<=5 ,表示单词的数量。接下来 n 行每行一个单词,只由小写字母组成,单词的长度至少为 1 ,最大为 2000
 

Output

仅一行,一个整数,最长公共子串的长度。
 

Sample Input

3
abcb
bca
acbc

Sample Output


首先一点,root到一个结点的pre包含这个节点的所有后缀。 
那么就很好做了!
对第一个串建立后缀自动机。然后跑匹配
到每个节点的len的最大值取最小值,然后再把所有最小值求最大值就是答案了
初始每个节点的ans[i]为step[i],为了防止路径超过根到当前点的最长路径
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
struct sam
{
	 int son[1000001][26],pre[1000001],step[1000001];
	 int ans[1000001],v[1000001],q[1000001];
	 int len[1000001];
	 int last,tot;
	 sam()
	 {
	      last=1;
	      tot=1;
	 }
	 inline void push_back(int v)
	 {
	      tot++;
	      step[tot]=v;
	 }
	 inline void prep()
	 {
	      int i;
	      for(i=1;i<=tot;i++)
	           ans[i]=step[i];
	      for(i=1;i<=tot;i++)
	           v[step[i]]++;
	      for(i=1;i<=tot;i++)
	           v[i]+=v[i-1];
	      for(i=tot;i>=1;i--)
	           q[v[step[i]]--]=i;
	 }//基数排序,确定树的遍历 
     inline void extend(int x)
     {
          push_back(step[last]+1);
          int p=last,np=tot;
          while(son[p][x]==0&&p!=0)
          {
               son[p][x]=np;
               p=pre[p];
          }
          if(p==0)
               pre[np]=1;
          else
          {
               int q=son[p][x];
               if(step[q]!=step[p]+1)
               {
                    push_back(step[p]+1);
                    int nq=tot;
                    //memcpy(son[nq],son[q],sizeof(son[q]));
                    int i;
                    for(i=0;i<=25;i++)
                         son[nq][i]=son[q][i];
                    pre[nq]=pre[q];
                    pre[q]=pre[np]=nq;
                    while(son[p][x]==q)
                    {
                         son[p][x]=nq;
                         p=pre[p];
                    }
               }
               else
                    pre[np]=q;
          }
          last=np;
     }
     inline void build()
     {
     	  string x;
          cin>>x;
          int lx=x.size();
          int i;
          for(i=0;i<=lx-1;i++)
               extend(x[i]-'a');
     }
     inline void solve()
     {
     	  memset(len,0,sizeof(len));
     	  string x;
          cin>>x;
          int lx=x.size();
          int p=1,tmp=0;
          int i;
          for(i=0;i<=lx-1;i++)
          {
               int xt=x[i]-'a';
               while(son[p][xt]==0&&p!=0)
                    p=pre[p];
               if(p==0)
               {
                    p=1;
                    tmp=0;
               }
               else
               {
                    tmp=min(tmp,step[p])+1;
                    p=son[p][xt];
               }
               len[p]=max(len[p],tmp);
          }
          for(i=tot;i>=1;i--)
               len[pre[q[i]]]=max(len[pre[q[i]]],len[q[i]]);
          for(i=1;i<=tot;i++)
               ans[i]=min(ans[i],len[i]);
     }
}sam;
int main()
{
	 int n;
	 scanf("%d",&n);
     sam.build();
     int i;
     sam.prep();
     for(i=2;i<=n;i++)
          sam.solve();
     int ans=0;
     for(i=1;i<=sam.tot;i++)
          ans=max(ans,sam.ans[i]);
     printf("%d\n",ans);
     return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值