GalaxyOJ-786 (AC自动机)

题目

Problem Description

这是一道很容易ac的简单的字符串题。
给你n个字符串,这个字符串是由小写字母组成。
有q个询问,每次给出x和y。
找到第x个字符串和第y个字符串的连续公共子串同时也是某个字符串的前缀的串(某个字符串是指第一个字符串到第n个字符串的任意一个,包含第x个和第y个)。问这样的串的最长长度是多少。

Input

第一行一个数字n,表示字符串的个数
接下来的n行给出了这n个字符串。
接下来是一个整数q,表示询问个数
接下来的q行,一行有两个整数表示x和y

20%的数据,n,字母总个数<=10
50%的数据,n,字母总个数<=100
100%的数据,n,字母总个数<=100000,q<=100

Output

共q行,一行一个整数,表示第q个询问的答案

Sample Input

3
aaa
baaa
caaa
2
2 3
1 2

Sample Output

3
3

分析

  • 题目就是给 n 个字符串,每次询问给你其中两个两个,问他们两个中是这 n 个串中某个的前缀的的公共连续子串最长可以是多少。
  • 看看题目写着“容易ac的题”,结果是道 AC自动机 的题 罒w罒。
  • 首先把这 n 个串建成一个字典树,然后像 AC自动机 那样弄好 fail 指针,然后对于每个询问的两个串,从每个串第一个字符开始都这样操作一遍——
    • 一直跳fail,直到它到了根节点
    • 把路径上的点打上标记
  • 这样下来,每个打上标记的点所代表的字符串肯定是 n 个串中某个的前缀,且是当前串的一个连续子串。
  • 然后把两个串打的标记记下来,比较一下那些两个串都打过标记的点,他们代表着符合要求(但不一定是最长)的串,然后拿出长度最长(即在字典树中深度最深)的那个长度即可。

程序

#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#define X (ch[i][j]-'a')
#define son(x) (to[fa][x])
using namespace std;
string ch[100001];
int n,i,j,k,g,xx,yy,ans,L,Q,fa,f[2][105][100005];
int head[100005],to[100005][30],cnt;
int l,r,dep[100005],q[100005],fail[100005];

void get_fail(){
    for (i=q[l=r=0]=0; l<=r; i=q[++l])
        for (j=0; j<26; j++) if (to[i][j]){
            q[++r]=to[i][j];
            for (fa=fail[i]; fa && !son(j); fa=fail[fa]);
            if (i>0 && son(j)) fail[to[i][j]]=son(j);
        }
        else to[i][j]=to[fail[i]][j];
}

void que(string x,int m){
    for (k=to[0][x[j=0]-'a'],L=x.length(); j<L; k=to[k][x[++j]-'a']){
        for (g=k; g; g=fail[g]){
            if (f[m^1][Q][g]){ans=max(dep[g],ans); break;}
            if (f[m][Q][g]) break;
            f[m][Q][g]=1;
        }
    }
}

int main(){
    scanf("%d",&n);
    for (i=1; i<=n; i++){
        cin >>ch[i];
        for (j=0,k=0,L=ch[i].length(); j<L; dep[k]=++j)
            k=(to[k][X] ? to[k][X] : (to[k][X]=++cnt));
    }
    get_fail();
    for (scanf("%d",&Q); Q--; ans=0){
        scanf("%d%d",&xx,&yy);
        que(ch[xx],0);
        que(ch[yy],1);
        printf("%d\n",ans);
    }
}

提示

  • 我开始用的是字符数组(平常我也是用字符数组的),可是交上去发现居然 RE ,原来是题目说 n,总字符数 都可以为 100000,然后极限数据就是全是一个字符的串,后来无奈用了 string ,然后就过了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值