poj2774,hdu1403 lcs 后缀数组,后缀自动机

        求两个字符串的最长公共字串。练习模板的题..用后缀数组就把两个串拼起来,两串之间和末尾添加分隔符,然后扫一遍height数组,判断一下sa[i],sa[i-1]是不是在两个串里,是就更新最大值,否则Continue.刚学后缀数组的时候poj上C++,G++都没问题来着,而且当时那个模板还是错的- ...今重写了下结果C++各种RE,G++倒是一次过。

       

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=420000;
int s[maxn],rs[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int n,m,k,tt;
char s1[maxn],s2[maxn];
int rank[maxn],height[maxn];
int l1,l2;
inline int idx(char s)
{
   return s-'a'+2;
}
void getheight(int n)
{
    int i,j,k=0;
    for (i=0; i<=n; i++) rank[sa[i]]=i;
    for (i=0; i<n; i++)
    {
        if (k) k--;
        int j=sa[rank[i]-1];
        while(s[i+k]==s[j+k]) k++;
        height[rank[i]]=k;
    }
}
void build_ss(int m,int n)
{
    n++;
    int i,*x=t,*y=t2;
    for (int i=0; i<m; i++) c[i]=0;
    for (int i=0; i<n; i++) c[x[i]=s[i]]++;
    for (int i=1; i<m; i++) c[i]+=c[i-1];
    for (int i=n-1; i>=0; i--)
      sa[--c[x[i]]]=i;
    for (int k=1; k<=n; k<<=1)
    {
        int p=0;
        for (i=n-k; i<n; i++) y[p++]=i;
        for (i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;

        for (i=0; i<m; i++) c[i]=0;
        for (i=0; i<n; i++) c[x[y[i]]]++;
        for (i=1; i<m; i++) c[i]+=c[i-1];
        for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for (i=1; i<n; i++)
        x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])? p-1 : p++;
        if (p>=n) break;
        m=p;
    }
}
bool diff(int x,int y)
{
    int a=min(x,y);
    int b=max(x,y);
    if (a<l1 && b>l1) return true;
    return false;
}
int main()
{
//    freopen("in.txt","r",stdin);

        while(~scanf("%s",s1))
        {

        scanf("%s",s2);
        l1=strlen(s1);
        l2=strlen(s2);
        for (int i=0; i<l1; i++)
        s[i]=idx(s1[i]);
        s[l1]=0;
        n=l1+1;
        for (int i=0; i<l2; i++)
        {
            s[n++]=idx(s2[i]);
        }
        s[n]=1;
        build_ss(128,n);
        getheight(n);
        int ans=0;
        for (int i=2; i<n; i++)
        {
            if (diff(sa[i],sa[i-1]))
            ans=max(ans,height[i]);
        }
        printf("%d\n",ans);
        }

    return 0;
}

         刚学后缀自动机的话,这题拿来练习也挺合适的。用第一个串构建后缀自动机,之后拿第二个传在自动机上跑一遍,逐字符扫描,当前状态按这个字符向下走的状态非空的话,就转移并且tmp++,否则就沿着失配路径找到第一个可以按当前的字符向下转移的状态并且把tmp更新成now->val+1,找不到的话就回到根节点,tmp赋值成0.每次执行完都用tmp去更新一下最大值ans,第二个传扫完后答案也就出来了。用后缀自动机就正常了-C++,G++都没什么问题,而且毕竟是O(n)的,效率比倍增的SA好多了。

         

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
#include <cmath>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=101000*4;
const int S=26;
int tot,len;
int n,m;
int wtop[maxn];
int c[maxn];
int k;
int ans;
struct node
{
    node *par,*go[S];
    int val,id;
}*tail,*root,que[maxn],*top[maxn];
char str[maxn],s[maxn],ss[maxn];
inline int idx(char s)
{
    return s-'a';
}
struct SAM
{
    void init(int m)
    {
        for (int i=0; i<=m; i++)
        {
            memset(que[i].go,0,sizeof que[i].go);
            que[i].id=que[i].val=0;
        }
        tot=0;
        len=1;
        root=tail=&que[tot++];
        root->id=0;
        ans=0;
    }
    void add(int c,int l)
    {
        node* p=tail;
        node* np=&que[tot++];
        np->val=l;
        np->id=tot-1;
        while(p && p->go[c]==NULL) p->go[c]=np,p=p->par;
        if (p==NULL) np->par=root;
        else
        {
            node *q=p->go[c];
            if (p->val+1==q->val) np->par=q;
            else
            {
                node *nq=&que[tot++];
                *nq=*q;
                nq->id=tot-1;
                nq->val=p->val+1;
                np->par=q->par=nq;
                while(p && p->go[c]==q) p->go[c]=nq,p=p->par;
            }
        }
        tail=np;
    }
    void debug_suff()
    {
        for (int i=0; i<tot; i++)
        {
            for (int c=0; c<S; c++)
            if (que[i].go[c])
            {
                cout<<que[i].id<<" "<<que[i].go[c]->id<<endl;
            }
        }
    }
    void debug_pre()
    {
        for (int i=1; i<tot; i++)
        {
            cout<<que[i].id<<" "<<que[i].par->id<<endl;
        }
    }
    void TopS()
    {
        memset(c,0,sizeof c);
        for (int i=0; i<tot; i++)
        c[que[i].val]++;
        for (int i=1; i<len; i++)
        c[i]+=c[i-1];
        for (int i=0; i<tot; i++)
        top[--c[que[i].val]]=&que[i],wtop[c[que[i].val]]=i;
    }
    int slove(char* s)
    {
         int res=0,tmp=0;
         node *p=root;
         int l=strlen(s);
         for(int i=0; i<l; i++)
         {
             int x=idx(s[i]);
             if (p->go[x])
             {
                 p=p->go[x];
                 tmp++;
             }
             else
             {
                 while(p && p->go[x]==NULL)p=p->par;
                 if (p)
                 {
                     tmp=p->val+1;
                     p=p->go[x];
                 }
                 else
                 {
                     p=root;
                     tmp=0;
                 }
             }
             res=max(res,tmp);
         }
         return res;
    }

}sam;
char s1[maxn],s2[maxn];
int main()
{
//    freopen("in.txt","r",stdin);
    while(~scanf("%s",s1))
    {
        scanf("%s",s2);
        int l1=strlen(s1);
        sam.init(2*l1);
        for (int i=0; i<l1; i++)
        sam.add(idx(s1[i]),len++);
        sam.TopS();
        printf("%d\n",sam.slove(s2));
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值