先构造AC自动机,完了以后对于x,y,先用x把x的所有子串标记了,再用y去寻找是否y的子串为也是x的子串,根据字典树的性质,这已经保证了一定是原先某字串的前缀了。一开始T掉我还以为数据卡的恨死,结果是小细节错误,while(v && !ch[v][c])写成了while(!ch[v][c])导致死循环,xushu教了几个细节,不用吧所有串压到一个char数组里后面再调取,直接记录每个串的末尾节点,然后从后往前遍历,而且对于每次操作不要清空val数组,只需贴上时间戳,判断即可,而且每次只要找到被标记的立刻break,能保证是最长的,也保证每次查询每个点只走一次。然而优化先后竟然都是31ms?应该是HDU评测机问题。
#include<cstdio>
#include<cstring>
#define maxl 100010
int n,m,sz,ans;
int a[maxl],bgn[maxl],val[maxl],fail[maxl],dis[maxl];
int endind[maxl],from[maxl];
int ch[maxl][26];
char s[maxl];
int max(int x,int y)
{
if(x>y)
return x;
else
return y;
}
void insert(char *s,int x)
{
int c,len=strlen(s),u=0;
for(int i=0;i<len;i++)
{
c=s[i]-'a';
if(!ch[u][c])
{
sz++;dis[sz]=dis[u]+1;
from[sz]=u;ch[u][c]=sz;
}
u=ch[u][c];
}
endind[x]=u;
}
void prework()
{
int len;
sz=0;ans=0;
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
memset(fail,0,sizeof(fail));
memset(dis,0,sizeof(dis));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
insert(s,i);
}
}
void getfail()
{
int u=0,v,head=0,tail=0,cur;
for(int c=0;c<26;c++)
if(ch[0][c])
a[++tail]=ch[0][c];
while(head!=tail)
{
head=(head+1)%maxl;
cur=a[head];
for(int c=0;c<26;c++)
if(ch[cur][c])
{
u=ch[cur][c];
/*if(!u)
{
ch[cur][c]=ch[fail[cur]][c];
continue;
}*/
tail=(tail+1)%maxl;
a[tail]=u;
v=fail[cur];
while(v && !ch[v][c])
v=fail[v];
fail[u]=ch[v][c];
}
}
}
void getin(int x,int i)
{
int u=endind[x],c,j;
while(u)
{
j=u;
while(j)
{
val[j]=i;
j=fail[j];
if(val[j]==i)
break;
}
u=from[u];
}
}
void getout(int y,int i)
{
int u=endind[y],c,j;
while(u)
{
j=u;
while(j && dis[j]>ans)
{
if(val[j]==i)
{
ans=max(dis[j],ans);
break;
}
j=fail[j];
}
u=from[u];
}
}
void mainwork()
{
getfail();
int x,y;scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
getin(x,i);
ans=0;
getout(y,i);
printf("%d\n",ans);
}
}
int main()
{
int cas;
scanf("%d",&cas);
for(int i=1;i<=cas;i++)
{
prework();
mainwork();
}
return 0;
}