CF1535F String Distance

这篇题解是给不会 SA 也想不出正解只会无脑暴力还想跑得比较不慢的人看的

先尝试直接暴力。先把字符集不同的字符串分开考虑,枚举两个串。然后挖掘一下这个 f 到底是什么。

  • f(si,sj)=1337si,sj 字符集不同。
  • f(si,sj)=1si,sj 字符集相同且存在一对 l,r,使得 si,sj1l 的字符相等且它们的 r|s| 的字符也相等,且 si,sj 有一个 [l+1,r1] 区间内字符有序。
  • f(si,sj)=2:字符集相同的其它情况。

那么对于同一个字符集的我们只需要计算 f 函数等于 1 的个数。

我们先找到 si,sj 的 LCP 和 LCF,然后判一下 si,sj 除去 LCP 和 LCF 后是否有序。判断有序在对于每个字符串线性预处理后可以 O(1) 判断。

使用哈希计算 LCP 和 LCF,时间复杂度为 O(n2log|s|)


然后考虑另一个暴力,就是换一个角度,想要 f(si,sj)=1,第一个暴力先考虑 si,sj1l 的字符相等且它们的 r|s| 的字符也相等,那我们是不是可以先考虑 [l+1,r1] 区间内字符有序这个限制呢?

既然要满足 [1,l],[r,|s|] 都相等,那么 l,r 离得越远就越容易满足。所以对于每个字符串都找出它的极长有序区间。假设当前 si[l+1,r1] 区间有序,那就在哈希表 hashl,r 中加入 [1,l] 的字符串连接上 [r,|s|] 后的字符串的哈希值。

那么对于当前字符串,先枚举每个 hashi,j 统计答案,再把当前字符串的极长有序区间加到哈希表里面即可。

但是注意这样会算漏,要把字符串序列反过来再跑一遍。

这样做的时间复杂度是 O(n|s|2) 的。


取阈值 2500 较为优秀,n2500 跑第一种暴力,n>2500 跑第二种暴力。

这其实已经不是根号分治了,因为第一种暴力是带 log 的(

#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 中国大陆许可协议进行许可。

posted @   zqs2020  阅读(72)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.