jzoj3913. 艰难的选择(B组——Day6)

本文介绍了一道算法竞赛题目“艰难的选择”的解决方案,该题要求从一系列按身高排列的人中选出男女人数相等且人数最多的连续子序列。通过使用前缀和的概念和优化技巧,提供了一个高效的算法实现。

jzoj3913. 艰难的选择(B组——Day6)

题目

Description

Yzx已经当过多次“媒人”了。他因此获得了许多经验。例如,距Yzx观察,身高相近的人似乎比较合得来。
Yzx在学校策划了一次大型的“非常男女”配对活动。对于这次活动的参与者,Yzx有自己独特的选择方式。他希望能选择男女人数相等且身高都很接近的一些人。这种选择方式实现起来很简单,他让学校的所有人按照身高排成一排,然后从中选出连续的若干个人,使得这些人中男女人数相等。Yzx当然希望他能选出的人越多越好,请告诉他最多可以选出多少人来。

Input

第一行有一个正整数n,代表学校的人数。
第二行有n个用空格隔开的数,这些数只能是0或1,其中,0代表一个男生,1代表一个女生。

Output

一个非负整数,表示最长的一段男女人数相等的子序列长度(如果不存在男女人数相等的子序列输出0)。

Sample Input

9
0 1 0 0 0 1 1 0 0

Sample Output

6

Data Constraint

30%的数据,n<=100。
50%的数据,n<=1000。
100%的数据,n<=100000。

解析

前缀和+优化

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

int n,maxn,k;
int a[100010];

int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		scanf("%d",&k);
		if (k==0) a[i]=a[i-1]-1;
			else a[i]=a[i-1]+1;
	}
	k=0;
	while (maxn<=n-k){
		int i=k+maxn+2;
		while (i<=n){
			if (a[i]-a[k]==0) maxn=max(maxn,i-k);
			i+=2;
		}
		k++;
	}
	printf("%d",maxn);
	return 0;
}
我们已经知道: - **预期输出**:`1657` - **实际输出**:`1665` 说明当前代码多统计了 $1665 - 1657 = 8$ 个单词。 这意味着在文本处理过程中,有 **8 个不应被当作有效单词的字符串被错误地保留了下来**,或者 **某些符号未正确替换为空格导致本应合并的词被拆成多个词**。 --- 我们要重新审视问题要求: > 不包括空格和**除单引号以外的标点符号**(!"#$%&()*+,./:;<=>?@[]^_{|}~\n) > 这些符号和空白符一样都是单词的分隔符 > 数字及多个连续的数字按一个单词计算 > 类似 I'll 和 wife's 等单词当成一个词计 关键点: - 只允许:字母(a-z, A-Z)、数字(0-9)、以及用于缩写的 `'` - 特别注意:不能让 `'` 出现在词首或词尾,比如 `'s`、`'t`、`'re` 被单独切出来就会变成无效单词但依然计入 → 这可能是问题所在! --- ### ❗问题分析:单引号孤立情况 当前正则表达式:`[^a-zA-Z0-9']` 替换为 空格 → 它保留了所有字母、数字和 `'` 但是这会导致如下情况: 原文可能含有: ``` "It's a beautiful day," said the old man. ``` 经过 `re.sub(r"[^a-zA-Z0-9']", ' ', ...)` 后变成: ``` It's a beautiful day said the old man ``` 然后 `.split()` 得到: ```python ['It's', 'a', 'beautiful', 'day', 'said', 'the', 'old', 'man'] ``` 看起来没问题? 但如果是: ``` "Don't do that!" he shouted. ``` → 处理后:` Don't do that he shouted ` → 分词:`["Don't", "do", "that", "he", "shouted"]` ✅ 但如果出现撇号独立出现,例如: ``` The ’80s were wild. He’s back. She said ‘no’. ``` 注意这里的引号可能是全角、方向性引号(如 `‘`, `’`),它们不是 ASCII 的 `'`(U+0027),而是 Unicode 的 LEFT/RIGHT SINGLE QUOTATION MARK(U+2018/U+2019)。 这些字符在我们的正则表达式中没有被过滤,因为它们不在 `[^a-zA-Z0-9']` 的排除范围内?错! 实际上它们**也不属于 `a-zA-Z0-9'`**,所以应该被替换为空格 → 是安全的。 但我们必须确认:是否这些字符被正确识别? 更危险的是: ### ❌ 错误情况:孤立的撇号 `'` 导致无效单词 例如: ``` I can't believe it's already over. ``` → 正确分词:`['I', 'can't', 'believe', 'it's', 'already', 'over']` ✅ 但如果原文有: ``` He said 'no' and left. ``` → 经过清洗后:`He said 'no' and left` → `.split()` → `['He', 'said', "'no'", 'and', 'left']` 此时 `'no'` 左右带空格,中间是一个由单引号包围的单词,变成了 `"'no'"`,但 Python 字符串里它还是合法的 token。 然而更严重的问题出现在这种情形: ``` word1 ' word2 ``` → 清洗后:`word1 ' word2` → split → ['word1', "'", 'word2'] → 这里的单独 `'` 被当成了一个单词! 这就导致了多出一个“单词” —— 单独的一个撇号 `'` 所以:如果原始文本中存在 **孤立的单引号 `'`**,就会被保留下来成为一个长度为1的字符串,在 `split()` 后进入列表,从而被计入单词总数。 这就是造成 **多统计单词** 的根本原因! --- ### ✅ 解决方案:在清洗之后,过滤掉仅由 `'` 构成或无意义的项 我们需要在 `replace_txt` 中增加一步:**去除只包含撇号或无实质内容的“单词”** 修改后的代码如下: ```python import re def read_file(filename): with open(filename, 'r') as file: text = file.read() return text def replace_txt(text): # 将除了字母、数字、单引号之外的所有字符替换为空格 cleaned = re.sub(r"[^a-zA-Z0-9']", ' ', text) # 按空白切分 words = cleaned.split() # 过滤掉仅由单引号成的无效单词,如 "'", "''" 等 filtered_words = [word for word in words if not word.strip("'") == ""] return filtered_words if __name__ == '__main__': name = input().strip() txt = read_file('step7/' + name) result = replace_txt(txt) print(len(result)) ``` #### 🔍 关键解释: ```python [word for word in words if not word.strip("'") == ""] ``` - `word.strip("'")`:去掉字符串前后所有的 `'` - 如果结果为空字符串(比如原词是 `"'"` 或 `"'''"`),说明它是纯撇号 → 应剔除 - 否则保留(如 `"I'm"` → strip后是 `"Im"` ≠ "" → 保留) 例如: - `'` → strip后为 `""` → 被过滤 ✅ - `''''` → strip后为 `""` → 被过滤 ✅ - `I'm` → strip后为 `Im` ≠ "" → 保留 ✅ - `don't` → 保留 ✅ - `'hello'` → strip后为 `hello` ≠ "" → 保留 ✅ --- ### 🧪 效果验证 假设原来因孤立 `'` 而多了 8 个无效单词(正好对应差值 1665 - 1657 = 8),那么加上这个过滤条件后,应能准确输出 `1657` --- ### 最终修正版代码(推荐提交版本) ```python import re def read_file(filename): with open(filename, 'r') as file: text = file.read() return text def replace_txt(text): # 保留字母、数字、单引号,其余替换为空格 cleaned = re.sub(r"[^a-zA-Z0-9']", ' ', text) # 切分成单词 words = cleaned.split() # 过滤掉只有单引号成的无效项 return [w for w in words if w.strip("'") != ""] if __name__ == '__main__': name = input().strip() txt = read_file('step7/' + name) result = replace_txt(txt) print(len(result)) ``` --- ### 知识点(列出该代码中遇到的知识点) - **正则表达式否定字符集应用**:使用 `[^a-zA-Z0-9']` 精准匹配需替换的非法字符。 - **字符串 `strip(char)` 方法**:移除首尾指定字符,可用于判断是否为纯撇号单词。 - **列表推导式与条件过滤**:高效剔除无效token,保证统计准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值