2021牛客多校10 - Browser Games(哈希)

题目链接:点击查看

题目大意:给出 n n n 个字符串,对于每个 i i i ,输出最少需要用多少个前缀,可以表示第 1 ∼ i 1\sim i 1i 个字符串而不能表示第 i + 1 ∼ n i+1\sim n i+1n 个字符串

题目分析:银川原题的数据加强版,正着模拟的思路被推翻,看了蒋佬队伍的代码用哈希倒着模拟的,太妙了。

简单翻译一下题面就是,对于每个 i i i 来说,需要在第 1 ∼ i 1\sim i 1i 个字符串中分别找到一个前缀,满足:

  1. i i i 个前缀去重后数量最少
  2. i i i 个前缀不会作为前缀出现在第 i + 1 ∼ n i+1 \sim n i+1n 个字符串中

如果是用字典树正着考虑的话,不难看出每个前缀的长度是逐渐变短的,所以利用这个性质去模拟。

首先考虑 i = n i=n i=n 时的答案,不难想到将 n n n 个字符串的第一个字母去重后就是 a n s [ n ] ans[n] ans[n] 了。这里的 n n n 个首字母代表的本质上是 n n n 个长度为 1 1 1 的前缀。

然后考虑删除掉第 n n n 个字符串会造成的影响,对于第 n n n 个字符串的所有前缀 p r e pre pre 来说,如果在字符串 1 ∼ n − 1 1\sim n-1 1n1 中,存在着一个前缀 s s s p r e pre pre 相等,那么需要将 s s s 加长,这样 s s s 才不会作为前缀出现在第 n n n 个字符串中。

用哈希优化一下空间,上述做法的空间复杂度和时间复杂度就都是 O ( ∑ ∣ S ∣ ) O(\sum|S|) O(S) 的了

代码:

// Problem: Browser Games
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/11261/A
// Memory Limit: 65536 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map>
#define lowbit(x) (x&-x)
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{
	T f=1;x=0;
	char ch=getchar();
	while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	x*=f;
}
template<typename T>
inline void write(T x)
{
	if(x<0){x=~(x-1);putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e6+100;
struct Hash {
	static const int mod1=1e9+7,mod2=1e9+9;
	int x,y;
	Hash(int x=0,int y=0):x(x),y(y){}
	Hash operator+(const Hash& t)const {
		return Hash((x+t.x)%mod1,(y+t.y)%mod2);
	}
	Hash operator*(const Hash& t)const {
		return Hash(1LL*x*t.x%mod1,1LL*y*t.y%mod2);
	}
	LL val() {
		return 1LL*x*mod2+y;
	}
}base(2333,23333),h[N];
unordered_map<LL,vector<int>>mp;
char s[N][110];
int pos[N],ans[N];
int main()
{
#ifndef ONLINE_JUDGE
//	freopen("data.in.txt","r",stdin);
//	freopen("data.out.txt","w",stdout);
#endif
//	ios::sync_with_stdio(false);
	int n;
	read(n);
	for(int i=1;i<=n;i++) {
		scanf("%s",s[i]);
		h[i]=Hash(s[i][0],s[i][0]);
		mp[h[i].val()].push_back(i);
		pos[i]=0;
	}
	for(int i=n;i>=1;i--) {
		ans[i]=mp.size();
		int len=strlen(s[i]);
		Hash tmp;
		for(int j=0;j<len;j++) {
			tmp=tmp*base+Hash(s[i][j],s[i][j]);
			auto it=mp.find(tmp.val());
			if(it!=mp.end()) {
				for(auto k:it->second) {
					if(k!=i) {
						pos[k]++;
						h[k]=h[k]*base+Hash(s[k][pos[k]],s[k][pos[k]]);
						mp[h[k].val()].push_back(k);
					}
				}
				mp.erase(it);
			}
		}
	}
	for(int i=1;i<=n;i++) {
		printf("%d\n",ans[i]);
	}
	return 0;
}
### 联合活动 Chocolate 问题解题思路 #### 背景描述 在网的联合活动中,“Chocolate”问题是关于如何合理分配巧克力给朋友。具体来说,目标是通过K次切割将一块由个连续部分组成的巧克力分成K+1份,每一份都包含一些连续的部分。 #### 解决方案概述 为了有效地解决这个问题,可以采用动态规划的方法来寻找最优解法。该方法的核心在于定义状态转移方程以及初始化边界条件[^1]。 #### 动态规划实现细节 - **状态表示**:设`dp[i][j]`代表前i个块中做最j刀所能获得的最大价值。 - **初始条件**:当没有切分时(`j=0`),最大值即为整个区间的总和;对于其他情况,则需遍历所有可能的位置进行尝试。 - **状态转移**:对于每一个新的位置k,在其之前已经完成了一定数量的分割操作(j),此时需要计算从当前位置到起点之间的最小成本,并更新全局最优解。 ```python def max_chocolate_value(chunks, K): n = len(chunks) # 计算区间内的累积和用于快速求子数组之和 prefix_sum = [0] * (n + 1) for i in range(1, n + 1): prefix_sum[i] = prefix_sum[i - 1] + chunks[i - 1] dp = [[float('-inf')] * (K + 1) for _ in range(n)] # 初始化第一列 for i in range(n): dp[i][0] = prefix_sum[i + 1] for j in range(K + 1): # 遍历每一刀数目的可能性 for i in range(n): # 当前考虑到第几个chunk为止 if j == 0 or i < j: continue for p in range(i): # 尝试不同的最后一刀位置p cost = abs(prefix_sum[p + 1] - prefix_sum[i + 1]) dp[i][j] = max(dp[i][j], min(dp[p][j - 1], cost)) return dp[-1][-1] ``` 此算法的时间复杂度主要取决于三重循环结构O(N^3*K),其中N为chunks的数量,K为允许的最大切割次数。虽然看起来效率不高但对于题目规模而言是可以接受的。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Frozen_Guardian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值