題目大意:給予n個字符串,有q次查詢,對於每次查詢,求出x串與y串的最長公共子串長度,該子串至少要是n個串中的一個串的前綴。
首先對輸入的n個串建立字典樹,方便查詢前綴,然後對於每次查詢輸入的x與y,把x串與y串取出來,兩個串連接在一起,中間用個沒有出現過的字符隔開,對這個串套用後綴數組模板,然後對答案二分。
對於每個二分出來的mid,按照掃描一遍height數組,如果當前的height[i]的值大於mid,而且當前的兩個串分別是x與y的後綴,而且他們的公共前綴可以在字典樹中查詢到,那麼mid就是合法的。
#include <algorithm>
#include <bitset>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <math.h>
#include <set>
#include <vector>
using namespace std;
#define Max_N (100000 + 100)
struct node{
int next[27];
int v;
void init(){
v=0;
memset(next,-1,sizeof(next));
}
};
struct node L[Max_N];
int tot=0;
void add(char a[],int len){
int now=0;
for(int i=0;i<len;i++){
int tmp=a[i]-'a';
int next=L[now].next[tmp];
if(next==-1){
next=++tot;
L[next].v=-1;
L[next].init();
L[now].next[tmp]=next;
}
now=next;
L[now].v++;
}
}
int query(int a[],int len){
int now=0;
for(int i=0;i<len;i++){
int tmp=a[i];
int next=L[now].next[tmp];
if(next==-1)return 0;
now=next;
}
return L[now].v;
}
int n;
int k;
int a[Max_N];
int rank1[Max_N];
int tmp[Max_N];
bool compare_sa(int i, int j)
{
if(rank1[i] != rank1[j]) return rank1[i] < rank1[j];
else {
int ri = i + k <= n ? rank1[i + k] : -1;
int rj = j + k <= n ? rank1[j + k] : -1;
return ri < rj;
}
}
void construct_sa(int buf[], int s, int sa[])
{
int len = s;
for (int i = 0; i <= len; i++) {
sa[i] = i;
rank1[i] = i < len ? buf[i] : -1;
}
for ( k = 1; k <= len; k *= 2) {
sort(sa, sa + len +1, compare_sa);
tmp[sa[0]] = 0;
for (int i = 1; i <= len; i++) {
tmp[sa[i]] = tmp[sa[i-1]] + (compare_sa(sa[i-1], sa[i]) ? 1 : 0);
}
for (int i = 0; i <= len; i++) {
rank1[i] = tmp[i];
}
}
}
void construct_lcp(int buf[], int len, int *sa, int *lcp)
{
int h = 0;
lcp[0] = 0;
for (int i = 0; i < len; i++) {
int j = sa[rank1[i] - 1];
if (h > 0) h--;
for (; j + h < len && i + h < len; h++) {
if (buf[j+h] != buf[i+h]) break;
}
lcp[rank1[i] - 1] = h;
}
}
int sa[Max_N];
int lcp[Max_N];
char buf[Max_N];
int T;
int m;
vector<int> ss;
int p[Max_N];
struct point
{
int l; int r;
};
point p2[Max_N];
int s1[Max_N];
int s2[Max_N];
int a1[Max_N];
bool check(int x1, int x2, int l2, int r2)
{
if (0 <= x1 && x1 < l2 && l2 < x2 && x2 < r2) return true;
else return false;
}
int main()
{
cin >> T;
while (T--) {
scanf("%d", &m);
int len = 0;
tot = 0;
L[0].init();
for (int i = 0; i < m; i++) {
scanf("%s", buf);
//p[len2] = i+1;
int len2 = strlen(buf);
add(buf, len2);
p2[i+1].l = len;
for (int j = 0; j < len2; j++) {
p[len] = i + 1;
a1[len++] = buf[j] - 'a';
}
p[len] = 0;
p2[i+1].r = len;
a1[len++] = 10000+i;
}
int q;
scanf("%d", &q);
while (q--) {
int x, y;
scanf("%d%d", &x, &y);
int len3 = 0;
int len1 = 0;
for (int i = p2[x].l; i < p2[x].r; i++) {
s1[len1++] = a1[i];
a[len3++] = a1[i];
}
int len2 = 0;
a[len3++] = 1000;
for (int i = p2[y].l; i < p2[y].r; i++){
s2[len2++] = a1[i];
a[len3++] = a1[i];
}
/*for (int i = 0; i < len3; i++)
cout << a[i] << endl;*/
n = len3;
construct_sa(a, len3, sa);
construct_lcp(a, len3, sa, lcp);
int l1 = 0; int r1 = max(len1, len2);
int ans = 0;
while (true) {
int mid = (r1 - l1) / 2 + l1;
int flag = 0;
for (int j = 1; j < n; j++) {
if (lcp[j] >= mid && (check(sa[j], sa[j+1], len1, len3) || check(sa[j+1], sa[j], len1, len3))) {
if(query(a+sa[j], mid)) {
flag = 1;
break;
}
}
}
if (flag == 1) {
ans = mid;
l1= mid + 1;
}
else r1 = mid -1;
if (l1 > r1) break;
} cout << ans << endl;
}
}
return 0;
}