洛谷传送门
BZOJ传送门
题目描述
yz 是 Z 国的领导人,他规定每个地区的名字只能为 26 26 个小写拉丁字母的一个。由于地区数有可能超过 26 个,便产生了一个问题,如何辨别名字相同的地区?于是 yz 规定,一个地区的描述必须包含它的所有上级,且上级按次序排列。于是,一个地区的描述是一个字符串。比如说,一个地区的名字为 c c ,它的上级为 , b b 的上级为 , a a 没有上级,那么这个地区就描述为 。显然,这个描述同时包含了 c c 的上级 和 b b 的上级 的描述,分别为 ab a b 和 a a 。
值得注意的是,每个地区最多有一个上级,同一上级的地区之间名字不同,没有上级的地区之间名字不同。
现在,yz 对外公布了 个地区的描述,这些描述中包含了 Z 国所有地区的描述,并让你处理来访者的旅行问题。
现有 m m 对人访问这个国家,对于每对人,第一个人喜欢第 个描述中的第 j j 个地区,设这个地区描述为 ,第二个人喜欢第 k k 个描述中的第 个地区,设这个地区描述为 s2 s 2 。他们为了统一行程,决定访问描述为 s s 的地区(显然他们只关心地区的名字,并非是地区本身), 设 的长度为 t t , 需要满足以下条件:
1: t≤j t ≤ j , t≤l t ≤ l ;
1: s[1⋯t]=s1[j−t+1⋯j] s [ 1 ⋯ t ] = s 1 [ j − t + 1 ⋯ j ] , s[1⋯t]=s2[l−t+1⋯l] s [ 1 ⋯ t ] = s 2 [ l − t + 1 ⋯ l ] ;(即 s s 为 中 1 1 到 位与 s2 s 2 中 1 1 到 位的公共后缀)
2: t t 最大化。 为了不使输出过大,你只需把这个字符串按照如下生成的 进制数转成 10 10 进制后 mod 1000000007 m o d 1000000007 后输出:
a->0
b->1
.
.
.
z->25
比如地区 cab 被编码成 2×262+0×261+1×260=1353 2 × 26 2 + 0 × 26 1 + 1 × 26 0 = 1353 。
输入输出格式
输入格式:
第一行给定一个整数 n n
第 行:每 i+1 i + 1 行给定一个字符串 a[i] a [ i ] ,表示第 i i 个描述。
接下来一行一个整数
接下来 m m 行:每行给定四个整数 ,字母含义与题目描述一致。
输出格式:
共 m m 行,每行一个整数,表示答案字符串的编码。
输入输出样例
输入样例#1:
2
aabb
babb
2
1 3 2 3
1 4 2 4
输出样例#1:
1
1
说明
【样例说明】
询问 1 中的公共后有 ab 和 b,但是没有 ab 这个地区,只有 b 地区,所以只能选择 b 这个地区;
询问 2 中的公共后有 abb、bb 和 b,但是没有 abb 和 bb 这两个地区,只有 b 地区,所以只能选择 b 这个地区。
【数据范围】
设这个国家地区总数数为 tot(注意:输入的字符串总长度可能超过 tot!)
对于 30%的数据,满足 ;
对于 50%的数据,满足 tot,m,n≤1000 t o t , m , n ≤ 1000 ;
对于 80%的数据,满足 tot,m,n≤100000 t o t , m , n ≤ 100000 ;
对于 100%的数据,满足 tot,m,n≤1000000 t o t , m , n ≤ 1000000 ;
保证输入文件不超过 20MB。
解题分析
真是服了出题人, 这题目描述…
题意:给你 n n 个串, 次询问, 每次询问串 ai a i 的前 j j 位和串的前 l l 位的最长公共后缀, 并且其满足是某个串前缀的最长长度。
思路还是有点神的:某个串前缀一定是在我们构出的树上, 而前缀的后缀可以通过跳 fail f a i l 链得到。 所以我们答案即为 ai[j] a i [ j ] 和 ak[l] a k [ l ] 在 Trie T r i e 树上的 LCA L C A 处的值。
那么建出 Trie T r i e 树跑一边即可。
PS:博主用的倍增
LCA
L
C
A
会被卡空间,大家最好用树剖
LCA
L
C
A
…不过即使这样洛谷也可以过(数据太水)。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <queue>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define ll long long
#define MX 1000050
#define MOD 1000000007
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
ll hs[MX];
struct Node
{
int to[26];
}t[MX];
int arr, num, q, cnt;
int lent[MX], pos[MX << 1], dep[MX << 1], up[MX << 1][22];
char buf[MX];
std::queue <int> que;
namespace AC
{
void insert(const int &ord)
{
int l = std::strlen(buf + 1);
R int now = 0, val;
lent[ord] = l + lent[ord - 1];
for (R int i = 1; i <= l; ++i)
{
val = buf[i] - 'a';
if(!t[now].to[val])
t[now].to[val] = ++arr,
hs[t[now].to[val]] = (hs[now] * 26 + val) % MOD;
pos[++cnt] = t[now].to[val];
now = t[now].to[val];
}
}
void getfail()
{
R int now, tar;
for (R int i = 0; i < 26; ++i) if(t[0].to[i])
{
que.push(t[0].to[i]);
up[t[0].to[i]][0] = 0;
dep[t[0].to[i]] = 1;
}
W (!que.empty())
{
now = que.front(); que.pop();
for (R int i = 0; i < 26; ++i)
{
if(!t[now].to[i])//在这里倍增的是fail链, 所以依然要一直保存最近的可转移的位置。
{
t[now].to[i] = t[up[now][0]].to[i];
continue;
}
tar = t[now].to[i];
up[tar][0] = t[up[now][0]].to[i];
dep[tar] = dep[up[tar][0]] + 1;
for (R int i = 1; i <= 21; ++i)
{
if(up[tar][i - 1])
up[tar][i] = up[up[tar][i - 1]][i - 1];
else break;
}
que.push(tar);
}
}
}
IN int lca(R int x, R int y)
{
if(dep[x] < dep[y]) std::swap(x, y);
int del = dep[x] - dep[y];
for (R int i = 0; i <= 21; ++i)
{
if(del & (1 << i)) x = up[x][i], del -= (1 << i);
if(!del) break;
}
if(x == y) return x;
for (R int i = 20; ~i; --i)
{
if(up[x][i] != up[y][i])
x = up[x][i], y = up[y][i];
}
return up[x][0];
}
}
int main(void)
{
in(num); R int x, xl, y, yl, p1, p2;
for (R int i = 1; i <= num; ++i)
{
scanf("%s", buf + 1);
AC::insert(i);
} in(q);
AC::getfail();
W (q--)
{
in(x), in(xl), in(y), in(yl);
p1 = pos[lent[x - 1] + xl], p2 = pos[lent[y - 1] + yl];
printf("%lld\n", hs[AC::lca(p1, p2)]);
}
}