题意:
给你n个字符串。
然后q次询问,每次询问n个字符串的中两个字符串的一个最长后缀,满足这个后缀是n个字符串中一些串的前缀。求这个后缀是多少串的前缀。
思路:
建立字典树后再建立fail树。
字典树的每个节点代表着前缀,fail指针指向的位置代表着这个前缀在字典树上能匹配到的最长前缀,满足这个最长前缀为自己的后缀。
那么每次询问两个点,直接在fail树上找到它们的LCA,那就对应两个点的最长后缀且为其他串前缀的串。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 1e6 + 7;
struct Trie {
int son[30],val,fail;
}t[maxn];
int n,cnt;
char s[maxn];
int pos[maxn];
int head[maxn],to[maxn],nex[maxn],d[maxn],fa[maxn][30];//fa[i][j]代表i点的第2^j个祖先
int tot;
void add(int x,int y)
{
to[++tot] = y;
nex[tot] = head[x];
head[x] = tot;
}
void dfs(int x)
{
for(int i = head[x];i;i = nex[i])
{
int v = to[i];
if(!d[v + 1])
{
d[v + 1] = d[x + 1] + 1;
fa[v + 1][0] = x + 1;
dfs(v);
}
}
}
int lca(int x,int y)
{
if(d[x] < d[y])swap(x,y);//保证x是最深的
for(int i = 20;i >= 0;i--)
{
if(d[fa[x][i]] >= d[y])
x = fa[x][i];//将x移到和y一样的深度
}
if(x == y)//x移到y了,那说明x就是xy祖先
return x;
for(int i = 20;i >= 0;i--)
{
if(fa[x][i] != fa[y][i])
{
x = fa[x][i];
y = fa[y][i];//x,y一起上移
}
}
return fa[x][0];//此时x和y已经在同一个节点下了,返回x的父节点即可。
}
void init() {
for(int i = 0;i <= cnt;i++) {
memset(t[i].son,0,sizeof(t[i].son));
t[i].val = t[i].fail = 0;
}
memset(pos,0,sizeof(pos));
memset(d,0,sizeof(d));
memset(fa,0,sizeof(fa));
memset(head,0,sizeof(head));
tot = 0;
cnt = 0;
}
void insert(int id,char *s) {
int u = 0,len = strlen(s);
for(int i = 0;i < len;i++) {
int v = s[i] - 'a';
if(!t[u].son[v]) t[u].son[v] = ++cnt;
u = t[u].son[v];
t[u].val++;
}
pos[id] = u;
}
void getFail() {
queue<int>q;
q.push(0);t[0].fail = 0;
while(!q.empty()) {
int u = q.front();q.pop();
for(int i = 0;i < 26;i++) {
int v = t[u].son[i];
int Fail = t[u].fail;
if(!v) {
t[u].son[i] = t[Fail].son[i];continue; //直接让不存在的儿子指向失配位置
}
if(Fail == 0 && t[Fail].son[i] == v) t[v].fail = 0;
else t[v].fail = t[Fail].son[i];
q.push(v);
}
}
}
int query(int x,int y) {
x = pos[x] + 1;y = pos[y] + 1;
int z = lca(x,y) - 1;
return t[z].val;
}
int main() {
int n;
while(~scanf("%d",&n)) {
init();
for(int i = 1;i <= n;i++) {
scanf("%s",s);
insert(i,s);
}
getFail();
for(int i = 1;i <= cnt;i++) {
add(t[i].fail,i);
}
d[1] = 1;
fa[1][0] = 0;
dfs(0);
for(int i = 1;i <= 20;i++)
{
for(int j = 1;j <= cnt;j++)
{
fa[j][i] = fa[fa[j][i - 1]][i - 1];//j的2^i次方祖先等于j的2^i-1次方祖先的2^i-1次方祖先。
}
}
int m;scanf("%d",&m);
while(m--) {
int x,y;scanf("%d%d",&x,&y);
int ans = query(x,y);
if(!ans) printf("N\n");
else printf("%d\n",ans);
}
}
return 0;
}