题目描述
给定一个字符串 ss,定义它的 k*k* 前缀 prekpre**k 为字符串 s1…ks1…k,k*k* 后缀 sufksuf**k 为字符串 s∣s∣−k+1…∣s∣s∣s∣−k+1…∣s∣,其中 1≤k≤∣s∣1≤k≤∣s∣。
定义 Border(s)Borde****r(s) 为对于 i∈[1,∣s∣)*i*∈[1,∣*s*∣),满足 prei=sufi*p*r*e*i*=*s*u*f*i* 的字符串 preipre**i 的集合。Border(s)Borde****r(s) 中的每个元素都称之为字符串 ss 的 borderborder。
有 mm 组询问,每组询问给定 p,qp,q,求 ss 的 p*p* 前缀 和 q*q* 前缀 的 最长公共 borderborder 的长度。
输入格式
第一行一个字符串 ss。
第二行一个整数 mm。
接下来 mm 行,每行两个整数 p,qp,q。
输出格式
对于每组询问,一行一个整数,表示答案。若不存在公共 borderborder,请输出 00。
输入输出样例
输入 #1复制
aaaabbabbaa
5
2 4
7 10
3 4
1 2
4 11
输出 #1复制
1
1
2
0
2
输入 #2复制
zzaaccaazzccaacczz
3
2 18
10 18
3 5
输出 #2复制
1
2
0
看到border的定义很难不想到kmp,对于一个串,其border的border就是这个串的border。因此可以建出next树,即跑一遍kmp求出next数组然后在next[i] = j的情况下从j到i连一条边。最后对于每个询问求出p和q的LCA即可。
#include <bits/stdc++.h>
#define N 1000005
#define next nxt
using namespace std;
string s;
int m, n;
int f[N][25], dep[N], next[N];
void get_nxt() {
next[1] = 0;
for(int i = 2, j = 0; i <= n; i++) {
while(j > 0 && s[i] != s[j + 1]) j = next[j];
if(s[i] == s[j + 1]) j++;
next[i] = j;
f[i][0] = j;
dep[i] = dep[j] + 1;
}
}
int lca(int p, int q) {
if(dep[p] < dep[q]) swap(p, q);
for(int i = 21; i >= 0; i--) if(dep[f[p][i]] >= dep[q]) p = f[p][i];
for(int i = 21; i >= 0; i--) if(f[p][i] != f[q][i]) p = f[p][i], q = f[q][i];
return f[p][0];//!
}
int main() {
dep[0] = 0, dep[1] = 1;
cin >> s;
n = s.size();
s = ' ' + s;
get_nxt();
for(int i = 1; i <= 21; i++) {
for(int j = 1; j <= n; j++) {
f[j][i] = f[f[j][i - 1]][i - 1];
}
}
cin >> m;
for(int i = 1; i <= m; i++) {
int p, q;
cin >> p >> q;
cout << lca(p, q) << endl;
}
return 0;
}