[题解]AT_abc272_f [ABC272F] Two Strings
思路
实际上对于一个字符串 进行一个 的操作本质上就是在 中截取一段长度为 的子串。
于是你不难想到把 拼起来,形成一个字符串 ,然后比较后缀。你发现这是对的,因为两个串的字典序大小从前往后比较的,因此你尽管是比较的后缀,本质上还是比较的两个子串。
比较后缀大小,直接 SA 得出 数组。再次回到题目中,要求 的数量,你发现这个条件等效于比较两个后缀 的大小。
但是直接枚举 是不现实的,但是你可以先将 的 值丢到一个桶里面,然后对于所有的 对答案的贡献就是在桶中 小于等于它本身 的值的数量。因此对桶做一次前缀和即可。
但是本题中有一个坑点在与字符串应当在 字符串交界处插入一个极小的字符,在最后插入一个极大的字符。
中间插入极小字符是因为当 时, 就会影响两个后缀的大小关系;最后插入极大字符的原因是,本题求的是小于等于的情况,因此当两个子串相同时也应该记录答案,但是在 SA 中, 的大小就会乱飘,为了使我们可以通过上述方法求答案我们应该保证当 时,后者排在后面。
Code
#include <bits/stdc++.h>
#define re register
#define int long long
using namespace std;
const int N = 1e6 + 10;
int len,n,m = 250,ans;
int sum[N];
int sa[N],rk[N],prk[N],tmp[N],cnt[N];
char a[N],b[N],s[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
signed main(){
len = read();
scanf("%s%s",a + 1,b + 1);
for (re int i = 1;i <= len;i++) s[++n] = a[i];
for (re int i = 1;i <= len;i++) s[++n] = a[i];
s[++n] = '&';
for (re int i = 1;i <= len;i++) s[++n] = b[i];
for (re int i = 1;i <= len;i++) s[++n] = b[i];
s[++n] = '|';
for (re int i = 1;i <= n;i++){
rk[i] = s[i];
cnt[rk[i]]++;
}
for (re int i = 1;i <= m;i++) cnt[i] += cnt[i - 1];
for (re int i = n;i;i--) sa[cnt[rk[i]]--] = i;
for (re int w = 1;w < n;w <<= 1){
int num = 0,p = 0;
for (re int i = n - w + 1;i <= n;i++) tmp[++num] = i;
for (re int i = 1;i <= n;i++){
if (sa[i] > w) tmp[++num] = sa[i] - w;
}
for (re int i = 1;i <= m;i++) cnt[i] = 0;
for (re int i = 1;i <= n;i++) cnt[rk[i]]++;
for (re int i = 1;i <= m;i++) cnt[i] += cnt[i - 1];
for (re int i = n;i;i--) sa[cnt[rk[tmp[i]]]--] = tmp[i];
for (re int i = 1;i <= n;i++) prk[i] = rk[i];
for (re int i = 1;i <= n;i++){
if (prk[sa[i]] == prk[sa[i - 1]] && prk[sa[i] + w] == prk[sa[i - 1] + w]) rk[sa[i]] = p;
else rk[sa[i]] = ++p;
}
if (p == n) break;
m = p;
}
for (re int i = 1;i <= len;i++) sum[rk[i]]++;
for (re int i = 1;i <= 1e6;i++) sum[i] += sum[i - 1];
for (re int i = 2 * len + 2;i <= 3 * len + 1;i++) ans += sum[rk[i]];
printf("%lld",ans);
return 0;
}
作者:WaterSun
出处:https://www.cnblogs.com/WaterSun/p/18262933
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理