最近碰到了几次 bitset 乱搞字符串匹配的情况,故写文以记之。

1. 算法简介

核心思想:假设文本串为 \(s\),则对字符集中的每一个字符 \(c\) 开一个大小为 \(|s|\) 的 bitset \(N_c\),记录 \(c\) 出现在 \(s\) 中的哪些位置。

用多个模式串 \(t\) 去匹配 \(s\),并且求出 \(t\) 在 \(s\) 中每一次出现的结束位置,那么有这样一个套路:开一个长度为 \(|s|\) 的 bitset \(M\) 作为答案,一开始每一位都为 \(1\)。\(M\) 的含义:所有为 \(1\) 的位为可能的结束位置。遍历 \(t\) 中的每一个字符 \(t_i\),并将 \(N_{t_i}\) 左移(数位从小到大是从右往左,而位置是从左到右,注意区分) \(|t|-i\) 位的结果 \(N'_{t_i}\) 按位与 \(M\),即 \(M\gets M\ \mathrm{bitand}\ N’_{t_i}\)。最后得到的 \(M\) 即为 \(t\) 在 \(s\) 中所有出现的结束位置。总的时间复杂度为 \(\mathcal{O}(\frac{|s|\sum|t|}{\omega})\)。

这实际上就是将暴力匹配用 bitset 优化了一下。对于 \(t_i\) 考虑对可能的结束位置的限制:只有 \(t_i\) 在 \(s\) 中的所有出现位置向右移动 \(|t|-i\) 位后的所有位置,才可能成为 \(t\) 最终的结束位置。而将所有限制合起来就可以得到最终的结束位置。

  • 冷知识:bitset 有数值类型的 _Find_first()_Find_next(x) 函数(后者如果没有找到下一个位置会返回 bitset 的大小)。这可以非常方便地帮助我们在 \(\mathcal{O}(\frac{n}{\omega}+c)\) 的复杂度内找到 bitset 中所有为 \(1\) 的位置。具体使用可以看例题 II。

废话不多说,来两道例题感受一下 bitset 的神奇之处。

2. 例题

I. CF914F Substrings in a String

题意简述:给出文本串 \(s\),多次询问 \(l,r,y\) 求 \(y\) 在 \(s[l:r]\) 中出现了多少次。带修。\(|s|,\sum |y|\leq 10^5\)。

太经典了。

注意到这个带修就很恶心,普通的 SAM 做不起来。接下来有两个选择:

  1. 巨大多难写的分块 SAM。这里安利一下 我的 SAM 学习笔记
  2. 注意到时限竟然有 6s,而数据范围只有 \(10^5\),于是 bitset 暴力硬莽就完事了嗷!!!!11

具体实现方法和上题几乎一模一样,带修直接修改 bitset,查询的时候将 \(M\) 分别右移 \(l+|y|-2\) 位和 \(r\) 位,使用类似前缀和的思想去掉区间带来的影响(即计算 \([l+|y|-1,n]\) 与 \([r+1,n]\) 的贡献差)。计算得到的两个 bitset 分别有多少 1,作差,再与 \(0\) 取最大值即可(因为 \(l+|y|-2\) 可能大于 \(r\),而 bitset 的 count 应该是 unsigned int,老坑了)。

这样我们就用非常简单的方法切掉了一道 *3000 的题目。

#include <bits/stdc++.h>
using namespace std; const int N=1e5;
const int S=26; int n,q,len,tp,l,r;
char s[N],t[N],ch;
bitset <N> c[S],ans; int main(){
scanf("%s%d",s,&q),n=strlen(s);
for(int i=0;i<n;i++)c[s[i]-'a'][i]=1;
while(q--){
scanf("%d%d",&tp,&l),l--;
if(tp==1)scanf("%s",&ch),c[s[l]-'a'][l]=0,c[(s[l]=ch)-'a'][l]=1;
else{
scanf("%d%s",&r,t),len=strlen(t),ans.set();
for(int i=0;i<len;i++)ans&=c[t[i]-'a']<<len-i-1;
cout<<max(0,(int)((ans>>l+len-1).count()-(ans>>r).count()))<<"\n";
}
}
return 0;
}

II. CF963D Frequency of String

题意简述:给出 \(s\),多次询问 \(k,m\) 求出 \(m\) 至少出现了 \(k\) 次的 \(s\) 的子串的最小长度。\(|s|,\sum |m_i|\leq 10^5\)。

这里给出一个引理:互不相同的长度之和为 \(M\) 的字符串的 \(\rm{endpos}\) 集合大小之和不超过 \(n\sqrt M\)。

证明:互不相同的长度为 \(L\) 的字符串 \(\rm{endpos}\) 集合大小之和为 \(n-L+1\),而 \(\sum L\leq M\) 意味着最多只有 \(\sqrt M\) 种长度,得证。

那么可以 SAM + 线段树合并 or ACAM or bitset 求出 endpos 集合,然后直接暴力枚举即可。如果使用 bitset 则要用到上面提到的 _Find_first()_Find_next(x) 函数,时间复杂度为 \(\mathcal{O}(\frac{|s|\sum |m|}{\omega}+|s|\sqrt {\sum m})\)

bitset,永远滴神!!!!!11111

#include <bits/stdc++.h>
using namespace std; const int N=1e5;
const int S=26; int n,q,len,k,cnt,p[N];
char s[N],t[N];
bitset <N> c[S],ans; int main(){
scanf("%s%d",s,&q),n=strlen(s);
for(int i=0;i<n;i++)c[s[i]-'a'][i]=1;
while(q--){
scanf("%d%s",&k,t),ans.set(),len=strlen(t),cnt=0;
for(int i=0;i<len;i++)ans&=c[t[i]-'a']<<len-i-1;
for(int it=ans._Find_first();it!=N;it=ans._Find_next(it))p[++cnt]=it;
int ans=1e9; for(int i=k;i<=cnt;i++)ans=min(ans,p[i]-p[i-k+1]);
cout<<(ans==1e9?-1:ans+len)<<endl;
}
return 0;
}

bitset 的妙用:乱搞字符串匹配的更多相关文章

  1. cf914F. Substrings in a String(bitset 字符串匹配)

    题意 题目链接 Sol Orz jry 和上一个题一个思路吧,直接bitset乱搞,不同的是这次有了修改操作 因为每次修改只会改两个位置,直接暴力改就好了 #include<bits/stdc+ ...

  2. hash进阶:使用字符串hash乱搞的姿势

    前言 此文主要介绍hash的各种乱搞方法,hash入门请参照我之前这篇文章 不好意思hash真的可以为所欲为 在开头先放一下题表(其实就是我题解中的hash题目qwq) 查询子串hash值 必备的入门 ...

  3. “盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

    黑白图像直方图 发布时间: 2017年7月9日 18:30   最后更新: 2017年7月10日 21:08   时间限制: 1000ms   内存限制: 128M 描述 在一个矩形的灰度图像上,每个 ...

  4. [小专题]另一种字符串匹配的思路——Shift-And算法

    吐槽:前两天打组队赛遇到一个字符串的题考了这个(见:http://acm.hdu.edu.cn/showproblem.php?pid=5972 ) 当时写了个KMP瞎搞然后TLE了(害),赛后去查了 ...

  5. 【BZOJ-4692】Beautiful Spacing 二分答案 + 乱搞(DP?)

    4692: Beautiful Spacing Time Limit: 15 Sec  Memory Limit: 128 MBSubmit: 46  Solved: 21[Submit][Statu ...

  6. SCOI 2013 密码 & 乱搞

    题意: Fish 是一条生活在海里的鱼.有一天他很无聊,就到处去寻宝.他找到了位于海底深处的宫殿,但是一扇带有密码锁的大门却阻止了他的前进.通过翻阅古籍,Fish 得知了这个密码的相关信息:1. 该密 ...

  7. zstu.4194: 字符串匹配(kmp入门题&& 心得)

    4194: 字符串匹配 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 206  Solved: 78 Description 给你两个字符串A,B,请 ...

  8. HDU 5716 带可选字符的多字符串匹配(ShiftAnd)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5716 [题目大意] 给出一个字符串,找出其中所有的符合特定模式的子串位置,符合特定模式是指,该子串 ...

  9. [51nod1532]带可选字符的多字符串匹配

    有一个文本串,它的长度为m (1 <= m <= 2000000),现在想找出其中所有的符合特定模式的子串位置. 符合特定模式是指,该子串的长度为n (1 <= n <= 50 ...

随机推荐

  1. JuiceFS 如何帮助趣头条超大规模 HDFS 降负载

    作者简介 王振华,趣头条大数据总监,趣头条大数据负责人. 王海胜,趣头条大数据工程师,10 年互联网工作经验,曾在 eBay.唯品会等公司从事大数据开发相关工作,有丰富的大数据落地经验. 高昌健,Ju ...

  2. [Beta]the Agiles Scrum Meeting 9

    会议时间:2020.5.24 21:00 1.每个人的工作 今天已完成的工作 成员 已完成的工作 issue yjy 撰写技术博客 tq 实现评测机获取评测状态功能 评测部分增加更多评测指标 wjx ...

  3. 大闸蟹的OO第二单元总结

    OO的第二单元是讲多线程的协作与控制,三次作业分别为FAFS电梯,ALS电梯和三部需要协作的电梯.三次作业由浅入深,让我们逐渐理解多线程的工作原理和运行状况. 第一次作业: 第一次作业是傻瓜电梯,也就 ...

  4. Noip模拟35 2021.8.10

    考试题目变成四道了,貌似确实根本改不完... 不过给了两个小时颓废时间确实很爽(芜湖--) 但是前几天三道题改着不是很费劲的时候为什么不给放松时间, 非要在改不完题的时候颓?? 算了算了不碎碎念了.. ...

  5. Spring Security 的注册登录流程

    Spring Security 的注册登录流程 数据库字段设计 主要数据库字段要有: 用户的 ID 用户名称 联系电话 登录密码(非明文) UserDTO对象 需要一个数据传输对象来将所有注册信息发送 ...

  6. Github点赞超多的Spring Boot学习教程+实战项目推荐!

    Github点赞接近 100k 的Spring Boot学习教程+实战项目推荐!   很明显的一个现象,除了一些老项目,现在 Java 后端项目基本都是基于 Spring Boot 进行开发,毕竟它这 ...

  7. hdu 1080 Human Gene Functions(DP)

    题意: 人类基因由A.C.G.T组成. 有一张5*5的基因表.每格有一个值,叫相似度.例:A-C:-3.意思是如果A和C配对, 则它俩的相似度是-3[P.S.:-和-没有相似度,即-和-不能配对] 现 ...

  8. Shadertoy 教程 Part 4 - 绘制多个2D图形和混入

    Note: This series blog was translated from Nathan Vaughn's Shaders Language Tutorial and has been au ...

  9. 【数据结构&算法】08-栈概念&源码

    目录 前言 栈的定义 定义 常见应用 栈的常见应用 进栈出栈变化形式 栈的抽象数据类型 栈的顺序存储结构及实现 栈的顺序存储结构 顺序栈 顺序栈的结构定义 两栈共享空间 栈的链式存储结构及实现 栈的链 ...

  10. Python之模块导入(不看会后悔系列)

    看到这个标题猜想大家内心OS: 什么辣鸡水文,划走划走~ 别急有干货! 静态导入(照顾新人) 假设现在有两个文件a,b在不同目录,b文件想引用a文件中的函数: # test_module/sub_mo ...