题目
Problem Description
这是一道很容易ac的简单的字符串题。
给你n个字符串,这个字符串是由小写字母组成。
有q个询问,每次给出x和y。
找到第x个字符串和第y个字符串的连续公共子串同时也是某个字符串的前缀的串(某个字符串是指第一个字符串到第n个字符串的任意一个,包含第x个和第y个)。问这样的串的最长长度是多少。Input
第一行一个数字n,表示字符串的个数
接下来的n行给出了这n个字符串。
接下来是一个整数q,表示询问个数
接下来的q行,一行有两个整数表示x和y20%的数据,n,字母总个数<=10
50%的数据,n,字母总个数<=100
100%的数据,n,字母总个数<=100000,q<=100Output
共q行,一行一个整数,表示第q个询问的答案
Sample Input
3
aaa
baaa
caaa
2
2 3
1 2Sample 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 ,然后就过了。