CF1535F String Distance
这篇题解是给不会 SA 也想不出正解只会无脑暴力还想跑得比较不慢的人看的
先尝试直接暴力。先把字符集不同的字符串分开考虑,枚举两个串。然后挖掘一下这个 到底是什么。
- : 字符集不同。
- : 字符集相同且存在一对 ,使得 的 的字符相等且它们的 的字符也相等,且 有一个 区间内字符有序。
- :字符集相同的其它情况。
那么对于同一个字符集的我们只需要计算 函数等于 的个数。
我们先找到 的 LCP 和 LCF,然后判一下 除去 LCP 和 LCF 后是否有序。判断有序在对于每个字符串线性预处理后可以 判断。
使用哈希计算 LCP 和 LCF,时间复杂度为 。
然后考虑另一个暴力,就是换一个角度,想要 ,第一个暴力先考虑 的 的字符相等且它们的 的字符也相等,那我们是不是可以先考虑 区间内字符有序这个限制呢?
既然要满足 都相等,那么 离得越远就越容易满足。所以对于每个字符串都找出它的极长有序区间。假设当前 的 区间有序,那就在哈希表 中加入 的字符串连接上 后的字符串的哈希值。
那么对于当前字符串,先枚举每个 统计答案,再把当前字符串的极长有序区间加到哈希表里面即可。
但是注意这样会算漏,要把字符串序列反过来再跑一遍。
这样做的时间复杂度是 的。
取阈值 较为优秀, 跑第一种暴力, 跑第二种暴力。
这其实已经不是根号分治了,因为第一种暴力是带 的(
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#define hashval(l, r) ((1ll * pre[k][(l)] * Pow[m - (r) + 1] * seed + suf[k][(r)]))
typedef unsigned long long ull;
const ull seed = 233;
struct Node {
int l, r;
inline bool operator < (const int x) const {return l < x;}
};
int n, m, *seg[200005];
char *str[200005], tmp[200005];
ull Pow[200005], *pre[200005], *suf[200005];
long long ans;
std::unordered_map<ull, std::vector<int> > mp;
std::unordered_multiset<ull> hash[90][90];
int getlcp(int i, int j) {
int l = 0, r = m;
while (l < r) {
int mid = l + r + 1 >> 1;
if (pre[i][mid] == pre[j][mid]) l = mid;
else r = mid - 1;
}
return l;
}
int getlcs(int i, int j) {
int l = 1, r = m + 1;
while (l < r) {
int mid = l + r >> 1;
if (suf[i][mid] == suf[j][mid]) r = mid;
else l = mid + 1;
}
return l;
}
void calc1(const std::vector<int>& vec) {
ans -= vec.size() * 1ll * (vec.size() - 1) / 2 * 1335ll;
for (int i = 0; i < vec.size(); ++ i)
for (int j = i + 1; j < vec.size(); ++ j) {
int l = getlcp(vec[i], vec[j]) + 1, r = getlcs(vec[i], vec[j]) - 1;
if (seg[vec[i]][l] == seg[vec[i]][r] || seg[vec[j]][l] == seg[vec[j]][r]) -- ans;
}
}
void calc2(const std::vector<int>& vec) {
for (int i = 0; i <= m + 1; ++ i)
for (int j = 0; j <= m + 1; ++ j) hash[i][j].clear();
for (int k : vec) {
for (int i = 0; i <= m; ++ i)
for (int j = m + 1; j > i + 2; -- j)
ans -= hash[i][j].count(hashval(i, j));
int lst = 1;
for (int i = 2; i <= m; ++ i)
if (str[k][i - 1] > str[k][i])
hash[lst - 1][i].insert(hashval(lst - 1, i)), lst = i;
hash[lst - 1][m + 1].insert(hashval(lst - 1, m + 1));
}
}
int main() {
Pow[0] = 1ull;
for (int i = 1; i <= 200000; ++ i) Pow[i] = Pow[i - 1] * seed;
scanf("%d", &n);
for (int i = 1; i <= n; ++ i) {
str[i] = new char[200000 / n + 3];
pre[i] = new ull[200000 / n + 3];
suf[i] = new ull[200000 / n + 3];
seg[i] = new int[200000 / n + 3];
for (int j = 0; j < 200000 / n + 3; ++ j) str[i][j] = '\0';
scanf("%s", str[i] + 1);
if (i == 1) m = strlen(str[i] + 1);
pre[i][0] = suf[i][m + 1] = 0;
for (int j = 1; j <= m; ++ j)
pre[i][j] = pre[i][j - 1] * seed + str[i][j] - 'a' + 1;
for (int j = m, k = 1; j; -- j, k = 1ll * k * seed)
suf[i][j] = suf[i][j + 1] + 1ll * k * (str[i][j] - 'a' + 1);
int lst = 1, tot = 0;
for (int j = 2; j <= m; ++ j)
if (str[i][j - 1] > str[i][j]) {
++ tot;
for (int k = lst; k < j; ++ k) seg[i][k] = tot;
lst = j;
}
++ tot;
for (int k = lst; k <= m; ++ k) seg[i][k] = tot;
for (int j = 1; j <= m; ++ j) tmp[j] = str[i][j];
std::sort(tmp + 1, tmp + m + 1);
unsigned long long hash_value = 0ull, now = 1ull;
for (int j = 1; j <= m; ++ j, now = now * seed)
hash_value = hash_value + (tmp[j] - 'a' + 1) * now;
mp[hash_value].push_back(i);
}
ans = 1337ll * n * (n - 1) / 2ll;
if (n <= 2500) for (auto i : mp) calc1(i.second);
else {
for (auto i : mp) {
ans -= i.second.size() * 1ll * (i.second.size() - 1) / 2 * 1335ll;
calc2(i.second);
std::reverse(i.second.begin(), i.second.end());
calc2(i.second);
}
}
printf("%lld", ans);
return 0;
}
本文作者:zqs2020
本文链接:https://www.cnblogs.com/stinger/p/15840067.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步