bzoj2746 [HEOI2012]旅行问题 ( AC自动机 & fail树 +lca + hash )

bzoj2746 [HEOI2012]旅行问题

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=2746

题意:
给定n个字符串,共有m次询问,每次询问输入四个数S1,L1,S2,L2,表示求第S1个字符串长度为L1的前缀,和第S2个字符串长度为L2的前缀,的最长公共后缀,满足这个后缀是给定的某一个串的前缀。
为了不使输出过大,你只需把这个字符串按照如下生成的26进制数转成10进制后mod 1000000007后输出:
a->0
b->1

z->25
比如cab被编码成2 * 26^2 + 0 * 26^1 + 1 * 26^0 = 1353。

数据范围
m,n<=1000000;
保证输入文件不超过20MB。

题解:
fail树的应用。
两个串的最长公共后缀就是他们在fail树上的lca。

输入时把每个字符串插入Trie树时,顺便保存每个位置26进制后的值,以及每个位置对应Trie树上点的编号。
求fail时就干脆向倍增求lca一样,fail[u][p]就是u点向上跳2^p步得到的点。
最后直接输出lca的26进制hash值。

刚好100行☆
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
const int N=1<<20;
const int mod=1000000007;
const int P=21;
queue<int> Q;
int n,m,ch[N][26],fail[N][25],dep[N],tail=0,cnt=0,pos[N<<1],lenth[N];
char str[N];
LL Hash[N];
void insert()
{
    int len=strlen(str);
    int p=0; LL mark=0;
    for(int i=0;i<len;i++)
    {
        int c=str[i]-'a';
        if(!ch[p][c])
        {
            ch[p][c]=++tail;
            Hash[ch[p][c]]=(((Hash[p]*26)%mod)+c)%mod;
        }
        pos[++cnt]=ch[p][c];    
        p=ch[p][c];
    }   
}
void getfail()
{
    dep[0]=0;
    for(int i=0;i<26;i++)
    if(ch[0][i])
    {
        fail[ch[0][i]][0]=0;
        dep[ch[0][i]]=1;
        Q.push(ch[0][i]);
    }
    while(!Q.empty())
    {
        int top=Q.front(); Q.pop();
        for(int i=0;i<26;i++)
        {
            if(!ch[top][i])
            {
                ch[top][i]=ch[fail[top][0]][i];
                continue;
            }
            int u=ch[top][i];
            fail[u][0]=ch[fail[top][0]][i];
            dep[u]=dep[fail[u][0]]+1;
            for(int j=1;j<P;j++)
            fail[u][j]=fail[fail[u][j-1]][j-1];
            Q.push(u);
        }
    }   
}
int getlca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;d;d>>=1,i++)
    if(d&1) u=fail[u][i];
    if(u==v) return u;
    for(int p=P-1;p>=0;p--)
    {
        if(fail[u][p]!=fail[v][p])
        {
            u=fail[u][p];
            v=fail[v][p];   
        }
    }   
    return fail[u][0];
}
int main()
{
    scanf("%d",&n);
    memset(fail,0,sizeof(fail));
    lenth[0]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str);
        insert();
        lenth[i]=strlen(str);
        lenth[i]+=lenth[i-1];
    }
    getfail();
    scanf("%d",&m);
    while(m--)
    {
        int p1,l1,p2,l2;
        scanf("%d%d%d%d",&p1,&l1,&p2,&l2);
        int t1=pos[lenth[p1-1]+l1]; int t2=pos[lenth[p2-1]+l2];
        int lca=getlca(t1,t2);
        printf("%lld\n",Hash[lca]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值