Link:https://www.luogu.com.cn/problem/P9576
知识点:Z 函数,哈希,枚举,数数,序列数据结构
本质上是个串串枚举题,但是用序列数据结构优化了一下(
你说的对,但是:::
昨天晚上看到室友在玩一个叫千恋万花的游戏,我就好奇的凑上去看了下。他好像在推一个巫女样子的角色,白头发顶着对兽耳,于是我凑上去和他分享,我说「原神里也有巫女,比你这个好看多了」。
我室友让我闭嘴,别影响他打游戏。我想了想的确,影响别人打游戏是不好的,于是我就不说话了。
后来我室友换了个叫什么茉子的角色,是个忍者的样子,我看到了这是真的忍不住了。我立刻手机打开了原神,冲上去和我室友说到「原神里也有好多当忍者的角色诶」,说着将手机拿过去想给他看看...
不知道这家伙是不是因为剧情推不下去就火气很大,在这时唰的一下站了起来,用手不礼貌的指着我的脸大喊,能不能别用你那破原神影响我。我听着他的话感到一阵羞辱,我好心和他分享,他却如此这般。
想着这样自私的室友,我也就没了和他争吵的念头,我就回自己的座位去了。随后我只是在位子上嘀咕着,柚子厨是这样的,他就跟火药桶被点炸了一样,立刻冲过来夺了我的手机。
他掐着我的手机怼着我的脸,姿势就像握着板砖一样,很是吓人。他假装义正言辞的跟我说,让我别拿原神这种游戏来恶心他。我低着头一眼不发,他也渐渐放下了持着我手机的手臂。
僵持了半分钟,他说他平时说话糙了点,但是对人一直很友善,刚刚他走了单身线才这么冲动,希望我别生气。我一时无言以对,就说道「没事,原神里优菈也是...」
我话还没说完,他青筋又暴起来,一下子把我的手机摔在了地上...
说真的,现在我才认清我室友自私自利的嘴脸,根本不值得我给他分享好东西。
简述
给定两个仅由小写字母构成的字符串 \(s, t\),求有多少组 \(l, r, l', r'\),满足下列条件:
- \(1\le l\le r\le |s|\)。
- 设 \(s\) 删去子串 \(s[l:r]\) 后变为 \(s'\),有:\(1\le l'\le r'\le |s'|\)。
- \(s'[l':r'] = t\)。
\(1\le |s|\le 4\times 10^5\),\(1\le |t|\le 2\times 10^5\)。
2S,512MB。
分析
先解决一种简单的情况。若最终得到的 \(s'[l':r']\) 即为 \(s\) 中原来就是与 \(t\) 匹配的连续的子串 \(s[i:i + |t| - 1]\),则仅需确定删去的 \(l, r\),考虑 \(l,r\) 只可能同时出现在 \(s[i:i + |t| - 1]\) 的一侧且可以相等,显然方案数为 \(\frac{(i - 1)\times i}{2}+ \frac{(n - (i + |t| - 1))(n - (i + |t| - 1) + 1)}{2}\) 种。
然后考虑 最终得到的 \(s'[l':r']\) 在 \(s\) 中不连续的情况,即 \(s'[l':r']\) 可以被拆成 \(t\) 的一段前缀 \(\operatorname{pre}\) 和一段后缀 \(\operatorname{suf}\),两者在 \(s\) 中保持前后关系且不相邻,考虑枚举其中的一方并求可以拼成 \(t\) 的另一方的数量。
记 \(\operatorname{p}_1(i)\) 表示后缀 \(s[i:|s|]\) 与 \(t\) 的最长真公共前缀长度,\(\operatorname{p}_2(i)\) 表示前缀 \(s[1:i]\) 与 \(t\) 的最长真公共后缀长度。 \(\operatorname{p}_1\) 和 \(\operatorname{p} _2\) 可以通过对 \(s, t\) 正串和反串分别求 Z 函数预处理得到。为什么强调是真前缀和真后缀呢?因为要保证 \(\operatorname{pre}\) 和 \(\operatorname{suf}\) 都不能是空的,这样限制方便下一步求贡献。
则以 \(s_i\) 为开头可以组成长度为 \(1\sim \operatorname{p}_1(i)\) 的 \(\operatorname{pre}\)。对于以 \(s_i\) 开头长度为 \(l\) 的 \(\operatorname{pre}\),设可以与之拼成 \(t\) 的 \(\operatorname{suf}\) 以 \(s_j\) 为结尾,则 \(j\) 需要满足:
\[\begin{cases} j \ge i + |t|\\ \operatorname{p}_2(j)\ge |t| - \operatorname{p}_1(i) \end{cases}\]对于一个满足 \(j\ge i + |t|\) 的 \(j\),考虑它可以对某个 \(i\) 产生多少贡献。为了保证 \(\operatorname{pre}\) 和 \(\operatorname{suf}\) 都不能是空的,则有贡献的以 \(s_j\) 为结尾的 \(\operatorname{suf}\) 的长度限制为:
\[|t| - \operatorname{p}_1(i)\le |\operatorname{suf}|\le \operatorname{p}_2(j) \]由上式可知,对于所有以以 \(s_i\) 开头的 \(\operatorname{pre}\),所有满足上述条件的 \(\operatorname{suf}\) 数量之和,也即所有 \(j\) 对 \(i\) 的贡献之和即为:
\[\begin{aligned} &\sum_{j\ge i + |t|\land \operatorname{p}_2(j)\ge |t| - \operatorname{p}_1(i)} \operatorname{p}_2(j) - \left(|t| -\operatorname{p}_1(i)\right)\\ =& \left(\sum_{j\ge i + |t|\land \operatorname{p}_2(j)\ge |t| - \operatorname{p}_1(i)} \operatorname{p}_2(j)\right)- \left(|t| -\operatorname{p}_1(i)\right)\left(\sum_{j\ge i + |t|\land \operatorname{p}_2(j)\ge |t| - \operatorname{p}_1(i)} 1\right) \end{aligned}\]上面括号里的两部分可以以相似的方式分别求出来。发现满足条件的 \(j\) 是一个二维偏序的形式,这东西显然可以通过序列数据结构维护,即考虑在倒序枚举 \(i\) 的同时维护两个初始为空的树状数组,枚举到 \(i(1\le i\le |s| - |t|)\) 时将位置 \(\operatorname{p}_2(i + |t|)\) 加上 \(\operatorname{p}_2(i + |t|)\text{ or } 1\),查询区间 \(\left[|t| - \operatorname{p}_1(i), |t| - 1\right]\) 中的权值和即得括号内的值。
总时间复杂度 \(O\left(|s|\log |t|\right)\) 级别。
所以这东西本质上就是个枚举嘛呃呃
代码
//Ciallo~(∠・ω< )⌒★
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 4e5 + 10;
//=============================================================
int n, m, z1[kN], z2[kN], p1[kN], p2[kN];
char s[kN], t[kN];
LL ans;
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
struct Bit {
#define lowbit(x) ((x)&-(x))
int lim;
LL t[kN];
void Init(int n_) {
lim = n_;
}
void Insert(int pos_, LL val_) {
for (int i = pos_; i <= lim; i += lowbit(i)) {
t[i] += val_;
}
}
LL Sum(int pos_) {
LL ret = 0;
for (int i = pos_; i; i -= lowbit(i)) {
ret += t[i];
}
return ret;
}
LL Query(int l_, int r_) {
return Sum(r_) - Sum(l_ - 1);
}
#undef lowbit
} bit1, bit2;
void z_function() {
z1[1] = m;
for (int i = 2, l = 0, r = 0; i <= m; ++ i) {
if (i <= r && z1[i - l + 1] < r - i + 1) {
z1[i] = z1[i - l + 1];
} else {
z1[i] = std::max(0, r - i + 1);
while (i + z1[i] <= m && t[z1[i] + 1] == t[i + z1[i]]) ++ z1[i];
}
if (i + z1[i] - 1 > r) l = i, r = i + z1[i] - 1;
}
for (int i = 1, l = 0, r = 0; i <= n; ++ i) {
if (i <= r && z1[i - l + 1] < r - i + 1) {
p1[i] = z1[i - l + 1];
} else {
p1[i] = std::max(0, r - i + 1);
while (i + p1[i] <= n && s[i + p1[i]] == t[p1[i] + 1]) ++ p1[i];
}
if (i + p1[i] - 1 > r) l = i, r = i + p1[i] - 1;
}
std::reverse(t + 1, t + m + 1);
std::reverse(s + 1, s + n + 1);
z2[1] = m;
for (int i = 2, l = 0, r = 0; i <= m; ++ i) {
if (i <= r && z2[i - l + 1] < r - i + 1) {
z2[i] = z2[i - l + 1];
} else {
z2[i] = std::max(0, r - i + 1);
while (i + z2[i] <= m && t[z2[i] + 1] == t[i + z2[i]]) ++ z2[i];
}
if (i + z2[i] - 1 > r) l = i, r = i + z2[i] - 1;
}
for (int i = 1, l = 0, r = 0; i <= n; ++ i) {
if (i <= r && z2[i - l + 1] < r - i + 1) {
p2[i] = z2[i - l + 1];
} else {
p2[i] = std::max(0, r - i + 1);
while (i + p2[i] <= n && s[i + p2[i]] == t[p2[i] + 1]) ++ p2[i];
}
if (i + p2[i] - 1 > r) l = i, r = i + p2[i] - 1;
}
std::reverse(p2 + 1, p2 + n + 1);
}
void Solve1() {
for (int i = 1; i <= n; ++ i) {
if (p1[i] == m) {
int l1 = i - 1, l2 = n - (i + m) + 1;
ans += 1ll * l1 * (l1 + 1) / 2ll;
ans += 1ll * l2 * (l2 + 1) / 2ll;
}
}
}
void Solve2() {
for (int i = 1; i <= n; ++ i) {
if (p1[i] == m) -- p1[i];
if (p2[i] == m) -- p2[i];
}
bit1.Init(m), bit2.Init(m);
LL sum = 0;
for (int i = n - m; i; -- i) {
if (p2[i + m]) bit1.Insert(p2[i + m], 1);
if (p2[i + m]) bit2.Insert(p2[i + m], p2[i + m]);
LL ret1 = bit1.Query(m - p1[i], m - 1);
LL ret2 = bit2.Query(m - p1[i], m - 1);
sum = ret2 - ret1 * (m - p1[i] - 1);
ans += sum;
}
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
scanf("%s", s + 1); n = strlen(s + 1);
scanf("%s", t + 1); m = strlen(t + 1);
z_function();
Solve1();
Solve2();
printf("%lld\n", ans);
return 0;
}
标签:suf,le,P9576,TAOI,int,Ciallo,kN,ge,operatorname
From: https://www.cnblogs.com/luckyblock/p/17977133