3.19 CW 模拟赛 T4. 字符串

news/2025/3/25 17:42:47/文章来源:https://www.cnblogs.com/YzaCsp/p/18788299

前言

其实这种在排序时应该靠前的, 比较难评

思路

这个这个真的比较这个这个, 这下这下了

显然 \(M = 2\) 是非常好的提醒
我们发现可以通过记录 \(?\) 的模式来匹配问题

但是正如我赛时感受到的, 这显然不是一个好的可供模拟的方法, 必须厉害一点啊
因此不难考虑到状压哪些地方是问号, 以此来统计个数

现在最大的问题是怎么去查询哪些串和这个串可以匹配
不难发现 \(?\) 位置包含非问号位置可以匹配, 包含一部分 \((\)可以为空\()\), 剩下的直接匹配也行

怎么写, 显然可以利用一车二进制, 这不困难因此不加赘述

所以这样做复杂度大概是 \(\mathcal{O} (nm 2^m)\)

写着写着就不出意外的出意外了
遇到这种情况, 我的构想是不完善的

? ? o ? o o ?
? o o o ? ? ?

这种情况下怎么判断这两个东西是否匹配
也就是说, 我们可能需要修改一下状压的方式, 改成只记录被提取的位置
但是这样还是有问题, 因为通配符仍然是不好识别的

显然这是一个很重要的问题

当前遇到的问题

如何快速统计所有

  • 通配符包含当前非通配符
  • 通配符不包含当前非通配符的部分相同

其中比较不好处理的是第二个部分
也就是需要处理无视一些地方之后, 剩下部分相同的字符串数量

但是这又有问题了, 你发现无视的地方需要用当前通配符和之前通配符的交集来处理, 然而我们不可能同时知道之前通配符的位置提取出来的结果

因此这些性质过于复杂, 简直不太可能是正确做法了

考虑推倒重来
假设当前是串 ? o o o ? ? ? \((\)记为串 \(p\)\()\), 那么什么串可以和它匹配?
分为以下两种

  • 通配符包含 \(p\) 中非通配符
  • 通配符不完全\((\)当然也可以完全不\()\)包含 \(p\) 中非通配符, 但是不包含的部分都对应相同

发现不好处理在于我们枚举通配符位置之后, 不好再去找「不包含的部分」了

这个时候 \(20 \rm{pts}\) 的做法是否能给予一些启示?
也就是我们能不能分开维护

  • 通配符位置对应数量及对应提取串
  • 单独提取出一部分对应的「通配符位置对应数量及对应提取串」

那么维护

  • 通配符不完全\((\)当然也可以完全不\()\)包含 \(p\) 中非通配符, 但是不包含的部分都对应相同

是否就可以先提取出串 \(p\) 对应的非通配符对应的串, 然后再去做

感觉这样很复杂但确实可行, 也就是维护外层表示到底考虑哪些位, 内层表示 这些位置 对应的 通配符位置 对应的 提取串的数量 和 总共的数量, 可以做完 \(60\), 甚至到 \(100\)

正常 \(\rm{hash}\) 做法

发现原来不好处理本质上是分析问题出了一些哮问题

于是考虑两串 \(s, t\) 相似仅当 \(\displaystyle \forall i, s_i = \textrm{?} \lor t_i = \textrm{?} \lor s_i = t_i\)
假设当前串为 \(t\), 如何高效统计相似 \(s\) 个数
对当前 \(t\) 中非 \(\textrm{?}\) 的部分枚举是对应字符还是通配符, 然后再对应匹配 \(\rm{hash}\)

如何更新
发现我们需要的信息是对一些位置提取出来的子串做匹配, 因此直接这么写就好了

代码
#include <bits/stdc++.h>
#define FOR(i, a, b) for (register int i = (a); i <= (b); ++i)
using namespace std;int n, m;
char str[10];
map<int, int> mp[65]; // mp[j] stores the count of hash values for mask j
int ans;// Function to hash a character
inline int HashChar(char ch) {return (ch == '?') ? 26 : (ch - 'a');
}int main() {// Read input valuesscanf("%d %d", &n, &m);// Process each stringFOR(i, 1, n) {scanf("%s", str);int has = 0, opt = 0, tt = -1;char tmp[10]; // Stores non-wildcard characters// Compute the mask and extract non-wildcard charactersFOR(j, 0, m - 1) {if (str[j] != '?') {opt |= (1 << j); // Set the bit for non-wildcard positionshas = (has << 1) | 1; // Update the has masktmp[++tt] = str[j]; // Store the character}}// If the string is all wildcards, it matches all previous stringsif (opt == 0) {ans += i - 1;} else {// Enumerate all possible subsets of the non-wildcard positionsFOR(j, 0, has) {int cnt = 0; // Compute the hash value for the current subsetfor (int k = 0; (1 << k) <= has; ++k) {if ((1 << k) & j) {cnt = cnt * 30 + 26; // Wildcard position} else {cnt = cnt * 30 + (tmp[k] - 'a'); // Non-wildcard position}}// If the hash value exists in the map, add its count to the answerif (mp[opt].count(cnt)) {ans += mp[opt][cnt];}}}// Update the map with all possible masks for the current stringFOR(j, 1, (1 << m) - 1) {int cnt = 0; // Compute the hash value for the current maskFOR(k, 0, m - 1) {if ((1 << k) & j) {cnt = cnt * 30 + HashChar(str[k]); // Include the character in the hash}}mp[j][cnt]++; // Increment the count for the hash value}}// Output the final answerprintf("%d\n", ans);return 0;
}

逆天 \(\rm{bitset}\) 做法

\(s\) 每一个位置处理 \(\displaystyle \forall i, s_i = \textrm{?} \lor t_i = \textrm{?} \lor s_i = t_i\) 对应的 \(t\) 的位置, 取交即可

具体的, 对每个字符 \(x\) 维护 \(t_i = x\) 对应的 \(t\) 的位置, 然后取交即可

代码
#include <bits/stdc++.h>
using namespace std;bitset <50000> now, las[6][26];
long long ans;
int n, m;
char ch;signed main () {scanf("%d%d", &n, &m);for (int i = 0, tmp; i < n; i++) {now.set (); getchar (), getchar ();for (int j = 0; j < m; j++) {ch = getchar ();if (ch != '?') {now &= las[j][ch - 'a'];las[j][ch - 'a'].set (i);}else for (int x = 0; x < 26; x++) las[j][x].set (i);}tmp = now.count ();ans += (tmp < 50000 ? tmp : i);}printf("%lld", ans);return 0;
}

容斥做法

最有学习价值的一集, 可惜题解太屎了
但是对于集训队爷来说, 这种题写题解都是浪费时间

首先形式化问题为

题意

定义长为 mm 的串 s,ts, t 相似, 仅当
i[1,m],si=tisi=?ti=?\forall i \in [1, m], s_i = t_i \lor s_i = \text{?} \lor t_i = \text{?}
给定 nn 个长为 mm 的串 sis_i, 求相似串对的个数

比较一眼的是 \(\rm{hash}\) 的做法, 上面已经讲过了, 凭我自己不太可能想得到枚举匹配情况, 但是先不扯那么多, 继续降下去

燃尽最后一点热爱, 这个题必须想出来!
首先 \(s, t\) 相似仅当 \(\forall i, s_i = t_i \lor s_i = \text{?} \lor t_i = \text{?}\)

你发现这种形式并不易于维护
考虑正难则反, 转化成不相似
\(s, t\) 不相似仅当 \(\exists i, s_i \neq t_i \land s_i \neq \text{?} \land t_i \neq \text{?}\)
这个东西可以转化来用容斥原理维护, 下面记 \(s \nsim t\) 表示 \(s, t\) 不相似

\[ \begin{align*} & [s \nsim t] \\ =& [\exists i, s_i \neq t_i \land s_i \neq \text{?} \land t_i \neq \text{?}] \\ =& \sum_{\mathbb{S} \subseteq \mathbb{U}} (-1)^{|S| + 1} \times \Big( [\forall i \in \mathbb{S}, s_i \neq t_i \land s_i \neq \text{?} \land t_i \neq \text{?}] \Big)^{\ast} \\ =& \sum_{\mathbb{S} \subseteq \mathbb{U}} (-1)^{|S| + 1} \times \begin{cases} 0 & \text{若 } \exists i \in \mathbb{S}, s_i = \text{?} \lor t_i = \text{?}^{\dagger} \\ [\forall i \in \mathbb{S}, s_i \neq t_i] & \text{若 } \nexists i \in \mathbb{S}, s_i = \text{?} \lor t_i = \text{?} \end{cases} \\ =& \sum_{\mathbb{S} \subseteq \mathbb{U}} (-1)^{|S| + 1} \times [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}] \times [\forall i \in \mathbb{S}, s_i \neq t_i]^{\ddagger} \\ =& \sum_{\mathbb{S} \subseteq \mathbb{U}} (-1)^{|S| + 1} \times [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}] \times \Big(1 - [\exists i \in \mathbb{S}, s_i = t_i]\Big) \\ =& \sum_{\mathbb{S} \subseteq \mathbb{U}} \bigg\{ (-1)^{|S| + 1} \times [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}] \bigg\} - \bigg\{ (-1)^{|S| + 1} \times [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}] \times [\exists i \in \mathbb{S}, s_i = t_i] \bigg\}^{\S} \\ =& \sum_{\mathbb{S} \subseteq \mathbb{U}} \alpha - \left\{ \alpha \times \sum_{\mathbb{T} \subseteq \mathbb{S}} (-1)^{|T| + 1} \times [\forall i \in \mathbb{T}, s_i = t_i] \right\} \\ =& \sum_{\mathbb{S} \subseteq \mathbb{U}} (-1)^{|S| + 1} \times [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}] - \sum_{\mathbb{S} \subseteq \mathbb{U}} [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}] \times \sum_{\mathbb{T} \subseteq \mathbb{S}} (-1)^{|S| + |T|} \times [\forall i \in \mathbb{T}, s_i = t_i]^{\P} \end{align*} \]


\(\ast :\) 到这一步仍然不好直接处理, 于是下面才开始分类讨论转化成单一条件
\(\dagger :\) 本质上是发现处理 \(s_i = \text{?} \lor t_i = \text{?}\) 是简单的, 因为这与串串之间并没有关系
\(\ddagger :\) 发现 \(\neq\) 不好做, 考虑搞到 \(=\), 于是简单转化成 \(1 - [\exists i \in \mathbb{S}, s_i = t_i]\), 同上面用二次容斥把 \(\exists\) 转化成 \(\forall\) 即可
\(\S :\) 以下记 \(\alpha = (-1)^{|S| + 1} \times [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}]\)
\(\P :\) 不难发现这个部分是易于统计的, 因此转化到对上 \((\)假设字符串集合为 \(\mathbb{P}\)\()\)

\[ \begin{align*} & \sum_{s \in \mathbb{P}, t \in \mathbb{P}, s \neq t} [s \nsim t] \\ =& \sum_{s \in \mathbb{P}, t \in \mathbb{P}, s \neq t} \sum_{\mathbb{S} \subseteq \mathbb{U}} (-1)^{|S| + 1} \times [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}] - \sum_{s \in \mathbb{P}, t \in \mathbb{P}, s \neq t} \sum_{\mathbb{S} \subseteq \mathbb{U}} [\forall i \in \mathbb{S}, s_i \neq \text{?} \land t_i \neq \text{?}] \times \sum_{\mathbb{T} \subseteq \mathbb{S}} (-1)^{|S| + |T|} \times [\forall i \in \mathbb{T}, s_i = t_i] \\ =& \sum_{\mathbb{S} \subseteq \mathbb{U}} (-1)^{|S| + 1} \times \left(\sum_{s \in \mathbb{P}} [\forall i \in \mathbb{S}, s_i \neq \text{?}]\right) \times \left(\sum_{s \in \mathbb{P}} [\forall i \in \mathbb{S}, s_i \neq \text{?}] - 1\right) - \sum_{s \in \mathbb{P}, t \in \mathbb{P}, s \neq t} \sum_{\mathbb{S} \subseteq \mathbb{U}} \sum_{\mathbb{T} \subseteq \mathbb{S}} (-1)^{|S| + |T|} \times [\forall i \in \mathbb{T}, s_i = t_i \neq \text{?}] \\ \end{align*} \]

前面部分易于模拟, 后面部分用 \(\rm{hash}\) 维护即可

发现写出代码之后不太对, 只能开始数据检验了

  • 发现空集不能被考虑
  • 一个地方的精度问题被忽视了
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;// 自定义哈希函数,避免哈希碰撞
struct custom_hash {size_t operator()(uint64_t x) const {static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();return x ^ FIXED_RANDOM;}
};signed main() {ios::sync_with_stdio(false);cin.tie(0);int n, m;cin >> n >> m;int part1 = 0, part2 = 0;vector<string> words(n);for (int i = 0; i < n; ++i) {cin >> words[i];}long long ans = 0;// 枚举所有可能的mask_i(严格匹配的位置集合)for (int mask_i = 0; mask_i < (1 << m); ++mask_i) {if (mask_i == 0) continue;int bits_i = __builtin_popcount(mask_i);int lsy = 0; // 现在好像也没感觉了, 时间会冲淡除了友情的一切for (int k = 0; k < n; ++k) {bool valid = true;// 检查mask_i的位置是否有`?`for (int l = 0; l < m; ++l) {if ((mask_i & (1 << l)) && words[k][l] == '?') {valid = false;break;}}if (valid) lsy++;}int sign = 1;if ((bits_i + 1) % 2 == 1) {sign = -1;}part1 += sign * lsy * (lsy - 1) / 2;// 枚举mask_j为mask_i的所有子集for (int mask_j = mask_i;; mask_j = (mask_j - 1) & mask_i) {if (mask_j == 0) break;unordered_map<uint64_t, int, custom_hash> cnt;// 遍历所有字符串,筛选有效字符串for (int k = 0; k < n; ++k) {bool valid = true;// 检查mask_i的位置是否有`?`for (int l = 0; l < m; ++l) {if ((mask_i & (1 << l)) && words[k][l] == '?') {valid = false;break;}}if (!valid) continue;// 计算当前字符串在mask_j位置的哈希值uint64_t hash_val = 0;for (int l = 0; l < m; ++l) {if (mask_j & (1 << l)) {// 每个字符用5位表示(足够覆盖26字母)hash_val |= (uint64_t)(words[k][l] - 'a') << (5 * l);}}cnt[hash_val]++;}// 计算容斥符号sign = 1;int bits_j = __builtin_popcount(mask_j);if ((bits_i + bits_j) % 2 == 1) {sign = -1;}// 累加对数到答案for (auto &p : cnt) {long long c = p.second;part2 += sign * c * (c - 1) / 2;}if (mask_j == 0) break;}}cout << (n * (n - 1) / 2) - (part1 - part2) << endl;return 0;
}

实现

维护, 提取, 原神, 启动!

这题比较 \(\rm{adhoc}\) 吧, 确实是一个很神奇的东西, 锻炼思维

分析问题也有可能不够简单需要重来

取交并补可以用 \(\rm{bitset}\) 优化

一般来说, 问题简化到单一条件更好做
容斥原理善于把交转成并, 恰好就可以做这个问题
主要还是要在推导过程中找到简化问题实现的方法

注意一下不要肌肉记忆去恶心某个人了, 好像他已经不理我了, \(\rm{win}\)
要有重头再来的勇气啊!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/904366.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2、切片

一:字符串的下标(索引)--重点 ​ Ⅰ:字符串的特性,被称为下标或者 sequence(序列) ​ Ⅱ:一个序列。若干元素组成 ​ Ⅲ:字符串的下标从0开始》标记每个元素的位置,用来获取元素》从左到右,从0开始> a[0],a[1] ,a[3]》可以用正数表示,也可以用负数表示》最…

WEBGL 学习使用代码

目录杂七杂八第一节 绘制出了一个点第二节 动态传递点数据第三节 缓冲区和画线第四节 彩色线段第五节 单个 buffer 渲染颜色第六节 抽离代码 & 画彩色三角形第七节 图元的七种绘制方式第八节 uniform 传值变换数据第九节 旋转矩阵三角函数矩阵的计算推导代码实现第十节 线框…

Kettle 版本这么多,到底该怎么选?

Kettle(Pentaho Data Integration)作为一款功能强大的开源 ETL(Extract, Transform, Load,即数据抽取、转换和加载)工具,拥有众多版本,这让许多用户在选择时犯了难。 1、提出问题 经常有群友提出使用kettle版本的问题,如下图所示:2、kettle版本 有许多的的历史版本,…

C++实验二

实验一#include <stdio.h>#include <stdlib.h>#include <time.h>#define N 5int main() {int number;int i;srand(time(0)); // 以当前系统时间作为随机种子for(i = 0; i < N; ++i) {number = rand() % 100 + 1;printf("20490042%04d\n", n…

ESP32 Audino 驱动12864点阵屏 自定义中文字库

一.安装u8g2 #include <Arduino.h> #include <U8g2lib.h>U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, /* clock=*/22, /* data=*/21); // ESP32 Thing, HW I2C with pin remappingvoid setup(void) {u8g2.begin();u8g2.enableU…

Linux 离线安装 lz4

前言:本文操作是在 CentOS-7 下执行的,不确定在其他 Linux 发布版是否能同样正常执行。1、检查前置依赖组件在安装 lz4 之前,需要确认已安装了相关依赖组件: gcc 。 rpm -qa | grep gcc前置依赖组件的具体离线安装方法请参考:CentOS-7离线安装gcc 2、下载lz4安装包 官方下…

sql语句把图片存入数据库

这是一个小的练习,目的是把图片以二进制字符串形式存入sql数据库表中,后续练习尝试在WINCC把其还原成图片。 在以前的数据库MyDB中新建一个数据表,有四个字段: imageID 类型为bigint,作为标识符,自增1 mydatetime 日期事件类型 imagename varchar(100)数据类型 imagedata…

FSRCNN:加速超分辨率卷积神经网络

作为一种成功的图像超分辨率 (SR) 深度模型,超分辨率卷积神经网络 (SRCNN) 在速度和恢复质量方面都表现出优于以往手工制作模型的性能。然而,高计算成本仍然阻碍了它需要实时性能 (24 fps) 的实际使用。在本文中,我们旨在加速当前的 SRCNN,并提出一种紧凑的沙漏形 CN…

GNSS测量实习

实 习 报 告学院:建筑工程与空间信息学院 专业:地理信息科学 实习性质:校内实习 实习单位:建筑工程与空间信息学院 指导教师:冯建迪目录 一、实习的性质和目的要求 二、实习的任务和内容 三、静态测量 3.1 静态测量简介 3.2作业流程 3.3注意事项 3.4 GPS 控制网设计…

花束搭配

提取公式:Ai+Aj>Bi+Bj 变形得:Ai-Bi+Aj-Bj>0#include<bits/stdc++.h> using namespace std; #define int long long const int N = 1e6 + 10; int n, m, k, cnt, ans; string s;void solve() {cin >> n;vector<int> a(n), b(n), c(n);for (int i = 0…

题解:P11955 「ZHQOI R1」覆盖

https://www.luogu.com.cn/article/20vbz4zk对于一颗线段树,它的结构如图所示。一定是先有红色,再有绿色,再有蓝色,再有紫色。如果靠前的颜色没有那么靠后的颜色不可能出现。我们先考虑上一层(黑色)都已经处理完,新的一层会有什么影响,即已知 \(f_{2^j}\) 求 \(f_{2^j+…