一本通字符串 哈希 KMP (待完善)

[BalticOI 2014 Day1Three Friends] P6739
直接枚举断点,通过分类讨论找到候选值(不包含断点的半个字符串)
使用字符串的Hash(指可以\(O(1)\)查询区间Hash值的Hash)判断这个字符串是否满足条件

点击查看代码
#include <stdio.h>
#include <string.h>
typedef unsigned long long ULL;
const int N = 2e6 + 5;
int n, m; char str[N];
ULL p[N][2], h[N][2];
inline ULL hash(int l, int r, bool t) { return h[r][t] - h[l - 1][t] * p[r - l + 1][t]; }
inline bool same(int l1, int r1, int l2, int r2)
{ return hash(l1, r1, 0) == hash(l2, r2, 0) && hash(l1, r1, 1) == hash(l2, r2, 1); } // 是否相同(双模Hash)
int main() {
	scanf("%d%s", &n, str + 1), m = n >> 1; // m 为半个字符串的长度
	if(!(n & 1)) return puts("NOT POSSIBLE"), 0;
	for(int i = p[0][0] = p[0][1] = 1; i <= n; i ++)
		p[i][0] = p[i - 1][0] * 23333, h[i][0] = h[i - 1][0] * 23333 + str[i],
		p[i][1] = p[i - 1][1] * 13331, h[i][1] = h[i - 1][1] * 13331 + str[i];
	int res = 0;
	for(int i = 1; i <= m + 1; i ++)
		if(same(1, i - 1, m + 2, m + i) && same(i + 1, m + 1, m + i + 1, n)) res = 1; // 枚举在右边的字符串
	for(int i = 1; i <= m + 1; i ++)
		if(same(1, i - 1, m + 1, m + i - 1) && same(i, m, m + i + 1, n)) 
			{ if(res == 1 && strncmp(str + 1, str + m + 2, m)) return puts("NOT UNIQUE"), 0; res = -1; } // 枚举左边的字符串
	if(res == 1) puts(str + m + 2);
	else if(res) str[m+1] = 0, puts(str + 1);
	else puts("NOT POSSIBLE");
	return 0;
}

[POI2010]KOR-BeadsP3498
枚举 k ,将每个子串放入一个 unordered_map 或手写Hash表来判重
如何解决区间翻转?处理Hash出租的时候顺着来一遍,再反着来一遍

点击查看代码
#include <stdio.h>
#include <string.h>
const int N = 2e5 + 3;
typedef unsigned long long ULL;
int n, a[N], res[N], tot, ans;
ULL p[N], h1[N], h2[N];
// inline ULL hash(int l, int r)
// { return (h1[r] - h1[l - 1] * p[r - l + 1]) ^ (h2[l] - h2[r + 1] * p[r - l + 1]); }
inline ULL hash1(int l, int r) { return h1[r] - h1[l - 1] * p[r - l + 1]; }
inline ULL hash2(int l, int r) { return h2[l] - h2[r + 1] * p[r - l + 1]; }
int h[N], nxt[N], idx; ULL key[N];
int stk[N], top;
int insert(ULL _key) {
	int hv = _key % N;
	for(int i = h[hv]; i; i = nxt[i]) if(key[i] == _key) return 0;
	stk[++ top] = hv, key[++ idx] = _key, nxt[idx] = h[hv], h[hv] = idx;
	return 1;
}
int main() {
	scanf("%d", &n);
	for(int i = *p = 1; i <= n; i ++)
		scanf("%d", a + i), p[i] = p[i - 1] * N, h1[i] = h1[i - 1] * N + a[i];
	for(int i = n; i; i --) h2[i] = h2[i + 1] * N + a[i];
	for(int k = 1; k <= n; k ++) {
		int sum = 0;
		for(int i = 0; i + k <= n; i += k)
			if(hash1(i + 1, i + k) == hash2(i + 1, i + k)) sum += insert(hash1(i + 1, i + k)); // 小心这个串本身是个回文串
			else sum += insert(hash1(i + 1, i + k)) && insert(hash2(i + 1, i + k)); // 只有都没有的时候才统计答案
		if(sum > ans) res[0] = k, tot = 1, ans = sum;
		else if(sum == ans) res[tot ++] = k;
		idx = 0; while(top) h[stk[top --]] = 0; // stk为用过的表头(优化时间复杂度)
	}
	printf("%d %d\n", ans, tot);
	for(int i = 0; i < tot; i ++) printf("%d ", res[i]);
	return 0;
}

[POI2010]ANT-AntisymmetryP3501

点击查看代码
// 枚举中间点,二分
#include <stdio.h>
#include <string.h>
const int N = 5e5 + 5;
typedef unsigned long long ULL;
int n; char str[N];
ULL p[N], h1[N], h2[N];
inline ULL hash1(int l, int r) { return h1[r] - h1[l - 1] * p[r - l + 1]; }
inline ULL hash2(int l, int r) { return h2[l] - h2[r + 1] * p[r - l + 1]; }
int main() {
	scanf("%d%s", &n, str + 1);
	for(int i = *p = 1; i <= n; i ++)
		p[i] = p[i-1] * 23333, h1[i] = h1[i-1] * 23333 + str[i] * 10;
	for(int i = n; i; i --) h2[i] = h2[i+1] * 23333 + (str[i] ^ 1) * 10;
	long long res = 0;
	for(int i = 1; i < n; i ++) {
		int l = 0, r = i > n - i ? n - i : i;
		while(l < r) {
			int mid = (l + r + 1) >> 1;
			if(hash1(i - mid + 1, i) == hash2(i + 1, i + mid)) l = mid;
			else r = mid - 1;
		}
		res += l;
	}
	printf("%lld\n", res);
	return 0;
}

[POI2006] OKR-Periods of WordsP3435

点击查看代码
#include <stdio.h>
#include <string.h>
const int N = 1e6 + 5;
int n, nxt[N], min[N]; char str[N]; // min为最短前后缀
int main() {
	scanf("%d%s", &n, str + 1);
	for(int i = 2, j = 0; i <= n; i ++) {
		while(j && str[i] != str[j + 1]) j = nxt[j];
		if(str[i] == str[j + 1]) j ++;
		nxt[i] = min[i] = j;
	}
	long long res = 0;
	for(int i = 1; i <= n; i ++) {
		if(min[nxt[i]]) min[i] = min[nxt[i]];
		if(min[i]) res += i - min[i];
	}
	printf("%lld\n", res);
	return 0;
}

BZOJ3620似乎在梦中见过的样子

点击查看代码
#include <stdio.h>
#include <string.h>
const int N = 1.5e4 + 5, inf = 0x3f3f3f3f;
int n, k, nxt[N], min[N]; // min[i]为从i到1的路径>=k的最小的
int res; char str[N];
int main() {
	scanf("%s%d", str + 1, &k), n = strlen(str + 1);
	for(int t = 0; t < n; ++ t) {
		nxt[0] = 0, min[0] = inf;
		for(int i = 2, j = 0; i <= n - t; ++ i) {
			while(j && str[t + i] != str[t + j + 1]) j = nxt[j];
			if(str[t + i] == str[t + j + 1]) ++ j;
			nxt[i] = j, min[j] = j < k ? inf : j < min[nxt[j]] ? j : min[nxt[j]];
			if((min[j] << 1) < i) res ++;
		}
	}
	printf("%d\n", res);
	return 0;
}

[NOI2014] 动物园P2375

点击查看代码
#include <stdio.h>
#include <string.h>
typedef long long LL;
const int N = 1e6 + 5, mod = 1e9 + 7;
int T, n, nxt[N], num[N]; char str[N];
int main() {
	scanf("%d", &T);
	while(T --) {
		int res = num[1] = 1;
		scanf("%s", str + 1), n = strlen(str + 1);
		for(int i = 2, j = 0; i <= n; ++ i) {
			while(j && str[i] != str[j + 1]) j = nxt[j];
			str[i] == str[j + 1] && (++ j);
			nxt[i] = j, num[i] = num[j] + 1;
		}
		for(int i = 2, j = 0; i <= n; ++ i) {
			while(j && str[i] != str[j + 1]) j = nxt[j];
			str[i] == str[j + 1] && (++ j);
			while((j << 1) > i) j = nxt[j]; // 递推找到第一个满足条件的
			res = LL(num[j] + 1) * res % mod;
		}
		printf("%d\n", res);
	}
	return 0;
}

[POI2012]OKR-A Horrible PoemP3538

点击查看代码
// 每次尝试除掉最小因数
// 如果[l,r-c]=[l+c,r]则c为循环节
#include <stdio.h>
#include <string.h>
const int N = 5e5 + 5;
typedef unsigned long long ULL;
int n, m; char str[N];
ULL p[N], h[N];
inline ULL hash(int l, int r) { return h[r] - h[l - 1] * p[r - l + 1]; }
int primes[N], minpf[N], cnt;
int main() {
	scanf("%d%s%d", &n, str + 1, &m);
	for(int i = *p = 1; i <= n; i ++)
		p[i] = p[i - 1] * 23333, h[i] = h[i - 1] * 23333 + str[i];
	for(int i = 2; i <= n; i ++) {
		if(!minpf[i]) primes[cnt ++] = minpf[i] = i;
		for(int j = 0; i * primes[j] <= n; j ++) {
			minpf[i * primes[j]] = primes[j];
			if(i % primes[j] == 0) break;
		}
	}
	for(int k = 0, l, r, len, ans; k < m; k ++) {
		scanf("%d%d", &l, &r), len = ans = r - l + 1;
		while(len > 1) {
			if(hash(l, r - ans / minpf[len]) == hash(l + ans / minpf[len], r)) ans /= minpf[len];
			len /= minpf[len];
		}
		printf("%d\n", ans);
	}
	return 0;
}
posted @ 2022-10-09 20:00  azzc  阅读(18)  评论(0编辑  收藏  举报