题意:给出n个字符串,m次查询,每次查询给两个字符串的编号,输出满足是其它字符串的前缀(一个就行)的这两个字符串的最长公共子串的长度。题意是比较简单易懂了。
题解:首先多个字符串又牵涉到匹配找公共子串的问题,就应该考虑AC自动机了,但这道题的问题不只是找最长公共子串,还要求这个子串是其它字符串的前缀。这里可以利用ac自动机fail指针的特点,回忆一下,fail指针是怎么来的,在trie树上查找单词时,如果当前字符的下一位失配,那么就从和它有同样前缀的位置开始从新找(和KMP一样的道理),一个字符的失败指针指向的也就是这个和它有同样前缀的位置,那么两个字符串的公共子串要满足是别的字符串的前缀,肯定这个公共子串的最后一位是有非根节点的失败指针的(理解一下这里),这个公共子串的长度也就是它的失败指针到根节点的距离。所以题解就显而易见了,将所有的字符串建ac机,对每一组询问的两个字符串x,y,我们将x串在ac机上跑一遍,它所有的点的fail指针都标记一下(定义一个mark变量就可以了),再用y串跑一遍,对于它能到的点的fail指针,如果被标记过的话,那就一定是x,y串的公共子串,只需要不断地维护长度最大就行了。至于记录长度,这里我们在建trie树的时候就可以记录每个点到根节点的距离,而不用再像普通的ac自动机一样在每个字符串的结尾标记。
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef struct node
{
node *next[26];
node *fail;
int sum;
int mark;//标记每个节点是否已经被访问过
int len;//每个节点到根节点的长度
}node;
node *root;
node* que[100010];
char str[maxn*10];
int index[maxn];
int ans;
void init(node *a,int ll)//初始化
{
for(int i=0;i<26;i++)
a->next[i]=NULL;
a->fail=NULL;
a->sum=0;
a->len=ll+1;
a->mark=0;
}
void Insert(char word[])//将新的字符串插入字典树
{
node *p=root;
for(int i=0;word[i];i++)
{
int x=word[i]-'a';
if(!p->next[x])
{
p->next[x]=(node *)malloc(sizeof(node));
init(p->next[x],p->len);
}
p=p->next[x];
}
p->sum++;
}
void build_fail()
{
int head,tail;
head=tail=0;
que[tail++]=root;
while(head<tail)
{
node* tmp=que[head];
head++;
for(int i=0;i<26;i++)
{
if(!tmp->next[i]) continue;
if(tmp==root) tmp->next[i]->fail=root;
else
{
node *p=tmp->fail;
while(p)
{
if(p->next[i])
{
tmp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==NULL) tmp->next[i]->fail=root;
}
que[tail++]=tmp->next[i];
}
}
}
void solve(char des[],char ff[])
{
int ans=0;
node *p=root;
for(int i=0;des[i]!='\0';i++)//遍历第一个数组
{
int x=des[i]-'a';
p=p->next[x];
node *temp;
temp=p;
while(temp!=root)//
{
temp->mark=1;
temp=temp->fail;//有非根节点的失败指针的就一定是别的字符串的前缀
}
}
p=root;
for(int i=0;ff[i]!='\0';i++)
{
int x=ff[i]-'a';
p=p->next[x];
node *temp;
temp=p;
while(temp!=root)
{
if(temp->mark==1&&temp->len>ans)
{
ans=temp->len;
}
temp=temp->fail;
}
}
p=root;
for(int i=0;des[i]!='\0';i++)//消除标记
{
int x=des[i]-'a';
p=p->next[x];
node *temp;
temp=p;
while(temp!=root)
{
temp->mark=0;
temp=temp->fail;
}
}
printf("%d\n",ans);
}
int main()
{
int t,n,m,a,b;
scanf("%d",&t);
while(t--)
{
root=(node*)malloc(sizeof(node));
init(root,-1);
scanf("%d",&n);
getchar();
int loca=0;
for(int i=0;i<n;i++)
{
index[i]=loca;
scanf("%s",str+loca);
Insert(str+loca);
loca+=strlen(str+loca)+1;
}
build_fail();
scanf("%d",&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
solve(str+index[a-1],str+index[b-1]);
}
}
return 0;
}