Codeforce 1200 E. Compress Words(字符串Hash + 暴力)

题目大意:有 n 个单词,按顺序把 n 个单词拼起来成一整个单词,拼起来的时候前一个单词的后缀 和后一个单词的前缀的最长相同部分要省略掉一个,例如:apple please 拼起来是 applese
所有单词总长不超过 1 0 6 10^6 106


题解:用字符串hash可以 O ( n ) O(n) O(n)处理, O ( 1 ) O(1) O(1)比较。因为每个串只会被扫一遍,可以直接暴力,预处理 O ( n ) O(n) O(n),暴力 O ( n ) O(n) O(n)查找最长相同部分。

由于字符串长达到了 1 0 6 10^6 106,单模hash 已经搞不了,要用更安全的双 hash,取 2 个较大的模数 mod1 和 mod2,以及一个素数 p (p 不用太大,单模的 p 也不用太大,模数一定要大)
(这题就作为字符串hash入门了)


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 3e6 + 10;
#define pll pair<long long,long long>
ll p = 201326611;
ll h = 402653189;
ll pw[maxn],hw[maxn];
ll hash1[maxn];
ll hash2[maxn];
ll tmp1[maxn],tmp2[maxn];
char s[maxn],t[maxn];
int n;
pll Hash(int l,int r,ll hash1[],ll hash2[]) {
	return pll((hash1[r] - hash1[l  - 1] * hw[r - l + 1] % h + h) % h,(hash2[r] - hash2[l - 1] * pw[r - l + 1] % p + p) % p);
}
int main() {
	hw[0] = pw[0] = 1;
	for(int i = 1; i < maxn; i++) {
		hw[i] = hw[i - 1] * 131 % h; 
		pw[i] = pw[i - 1] * 131 % p;
	}
	scanf("%d",&n);
	int tot = 1,lst = 1;
	hash1[0] = hash2[0] = 0;
	for(int i = 1; i <= n; i++) {
		if(i == 1) {
			scanf("%s",s + 1);
			tot = strlen(s + 1);
			continue;
		}
		scanf("%s",t + 1);
		int len = strlen(t + 1);
		for(int j = lst; j <= tot; j++) {
			hash1[j] = (hash1[j - 1] * 131 + 1ll * s[j]) % h;
			hash2[j] = (hash2[j - 1] * 131 + 1ll * s[j]) % p;
		}
		lst = tot;
		for(int j = 1; j <= len; j++) {
			tmp1[j] = (tmp1[j - 1] * 131 + 1ll * t[j]) % h;
			tmp2[j] = (tmp2[j - 1] * 131 + 1ll * t[j]) % p;
		}
		int pos = 0;
		for(int j = 1; j <= len && j <= tot; j++)
			if(Hash(1,j,tmp1,tmp2) == Hash(tot - j + 1,tot,hash1,hash2))
				pos = j;
		for(int j = 1 + pos; j <= len; j++)
			s[++tot] = t[j];
	}
	s[++tot] = 0;
	printf("%s\n",s + 1);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值