虽然不知道失配树是什么,看这题是为数不多的KMP练手的"能做的题"之一,就把这道题切了
题目
个人觉得题目已经非常简洁明了,感动,就不再重复题意
思路
KMP+LCA
先推一道题,对这题应该有帮助:传送门
KMP中,next[i]
是由next[1~i-1]
得到的,若next[i]
由next[j]
得到,我们看成i是j的子节点next数组求解完成后,我们就有了一棵树(这就是标签"树形结构"的原因)
题目要我们求的是某两个前缀的最长公共"border",其实就是求连个点的LCA,自己仔细想想
简单写一个倍增LCA,满分代码就出来了
关于KMP+倍增的事情这里不详细讲,在上面推的题有提到
这道题其实真的不难,应该是我做过为数不多的紫题里最水的一道,其实跟上面推的那道题是一个档次的
代码
直接抠了P3435的代码改一改就交了
#include <iostream>
#include <cmath>
#include <cstdio>
#define nn 1000010
#define rr register
#define ll long long
using namespace std;
int sread(char *s) {
int siz = 1;
do
s[siz] = getchar();
while(s[siz] < 'a' || s[siz] > 'z');
while(s[siz] >= 'a' && s[siz] <= 'z')
s[++siz] = getchar();
siz--;
return siz;
}
int read() {
char c = getchar();
int re = 0;
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0',
c = getchar();
return re;
}
int next[nn][30] ;
int siz[nn];
int dep[nn];
char s[nn];
int n , m;
int main() {
// freopen("P3435_3.in" , "r" , stdin);
n = sread(s);
next[1][0] = 0;
dep[0] = 0;
dep[1] = 1;
for(int i = 2 , j = 0 ; i <= n ; i++) {
while(j != 0 && s[i] != s[j + 1])
j = next[j][0];
if(s[j + 1] == s[i])
++j;
next[i][0] = j;
dep[i] = dep[j] + 1;
}
/* for(int i = 1 ; i <= n ; i++)
cout << dep[i] << '\t';
return 0;*/
int k = log(n) / log(2) + 1;
for(int j = 1 ; j <= k ; j++)
for(int i = 1 ; i <= n ; i++) {
next[i][j] = next[next[i][j - 1]][j - 1];
if(next[i][j] == 0)
siz[i] = j;
}
m = read();
while(m--) {
int u = next[read()][0] , v = next[read()][0];//注意这里一个小细节
// cout << u << '\t' << v << endl;
if(dep[u] > dep[v]) {
int tmp = u ; u = v ; v = tmp;
}
int t = log(dep[v]) / log(2);
for(int i = t ; i >= 0 ; i--)
if(dep[next[v][i]] >= dep[u])
v = next[v][i];
if(u == v) {
printf("%d\n" , u);
continue;
}
t = log(dep[v]) / log(2);
for(int i = t ; i >= 0 ; i--)
if(next[v][i] != next[u][i])
u = next[u][i],
v = next[v][i];
printf("%d\n" , next[u][0]);
}
return 0;
}