首页 > 其他分享 >CF914F Substrings in a String

CF914F Substrings in a String

时间:2023-09-15 16:59:52浏览次数:32  
标签:le 匹配 String SAM sum times Substrings right CF914F

知识点:bitset,SAM,根号分治

Link:https://codeforces.com/problemset/problem/914/F

一种在字符集较小情况下的多轮字符串匹配暴力的优化。

好久没写过单题的题解了格式都忘了、、、

简述

给定一仅包含小写字母的字符串 \(s\),给定 \(q\) 次操作,每次操作都是下列两种形式之一:

  • 将字符串给定位置 \(s_i\) 修改为字符 \(c\)。
  • 给定参数 \(l, r\) 和字符串 \(t\),询问 \(s\) 的子串 \(s[l : r]\) 中字符串 \(t\) 出现的次数。

\(1\le |s|\le 10^5\),\(1\le q\le 10^5\),\(\sum |t|\le 10^5\)。
6S,256MB。

分析

正经做法

\(\sum |t| \le 10^5\) 是非常重要的性质,望周知。

正经做法是考虑对字符串分块,对每块建 SAM,对于修改操作则暴力重构该块的 SAM,对于查询操作考虑被查询字符串长度 \(|t|\) 和块长 \(k\) 的关系:

  • 若 \(|t|\ge k\),则此类询问最多出现 \(\frac{\sum |t|}{k}\) 次,考虑每次暴力建出子串 \(s[l:r]\) 的 SAM 并匹配即可。处理此类询问所需时间复杂度上界为 \(O\left(n \times \frac{\sum |t|}{k} + \sum |t|\right)\) 级别。
  • 若 \(|t|\le k\),则分别考虑位于整块内的 \(t\) 和跨越两块的 \(t\)。位于整块内的直接在区间内的所有 SAM 上匹配;跨越两块的同样考虑暴力,对区间内相邻的两块分界线附近长度为 \(2\times |t|\) 的部分建出 SAM(或 KMP)并匹配。需要暴力建 SAM(或 KMP)的区间长度总和上限为 \(\sum \left(2\times \frac{n}{k}\times |t|\right)\le 2\times n\) 级别,则处理此类询问所需时间复杂度上界为 \(O\left(2\times n + \sum\left(\frac{n}{k}\times|t|\right)\right)\) 级别。

发现 \(n\) 和 \(\sum |t|\) 同级,当 \(k=\sqrt n\) 级别时总时间复杂度为 \(O\left(n\sqrt n\right)\) 级别,又给了 6S,感觉就能过了。

然而很难写并且需要大力卡常还要玄学调块长、、、

虽然思路挺一眼的但反正我是没写,吸吸。

为什么不全用 KMP 呢?因为 KMP 匹配复杂度是和原串长度有关的,而 SAM 只与匹配串长度有关,因为只保证了 \(\sum |t|\le 10^5\) 所以只能用 SAM。

不正经做法

然后是不正经但是爆踩标算做法。为了把常数超大的正解放过去给的超大时限就是用来乱搞的吸吸

发现字符集较小,有一种对较小字符集做子串匹配的 bitset 搞法。

开 26 个 bitset,记 \(f_{i, j} = [s_{j} = i]\),按顺序考虑匹配串 \(t\)(\(i\) 从 0 开始计数)的每一位:

  • 对于满足 \(f_{t_0, j}=1\) 的位置 \(j\),子串 \(s[j:]\) 可以与 \(t\) 至少匹配 1 位。
  • 对于满足 \(f_{t_0, j} = 1 \land f_{t_1, j + 1} = 1\) 的位置 \(j\),子串 \(s[j:]\) 可以与 \(t\) 至少匹配 2 位。
  • ……
  • 对于满足 \(f_{t_0, j} = 1 \land \dots \land f_{t_{|t| - 1}, j + |t| - 1} = 1\) 的位置 \(j\),子串 \(s[j : j + |t| - 1]\) 可以与 \(t\) 匹配。

上述过程实际上是在对位置的集合不断地取子集,显然可以通过维护的 bitset 实现。按照上述规则手玩一下发现所有匹配的位置即为 f[t[i]] >> i 的交。此时直接统计 f[t[i]] >> i 中 1 的个数即得整个原串中所有匹配位置。在此基础上再对 bitset 进行右移后作差即得区间内匹配位置。

特别地,要注意特判被匹配区间短于匹配串的情况。

修改 \(O(1)\) 级别,总时间复杂度上界为 \(O\left(\sum |t|\times \frac{|s|}{w}\right)\) 级别。

但是跑了 2.7S 吸吸

代码

不正经做法:

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n, q;
char s[kN];
std::bitset <kN> appear[26], ans;
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::cin >> s + 1; n = strlen(s + 1);
  for (int i = 1; i <= n; ++ i) appear[s[i] - 'a'].set(i);
  q = read();
  
  while (q --) {
    int opt = read();
    if (opt == 1) {
      int pos; char val;
      std::cin >> pos >> val;
      appear[s[pos] - 'a'].reset(pos);
      s[pos] = val;
      appear[s[pos] - 'a'].set(pos);
    } else {
      int l, r; std::string t;
      std::cin >> l >> r >> t;
      if (t.size() > r - l + 1) {
				std::cout << "0\n";
				continue;
			}
      ans.set();
      for (int i = 0, sz = t.size(); i < sz; ++ i) ans &= appear[t[i] - 'a'] >> i;
      std::cout << (ans >> l).count() - (ans >> (r - t.size() + 2)).count() << '\n'; 
    }
  }
  return 0;
}

标签:le,匹配,String,SAM,sum,times,Substrings,right,CF914F
From: https://www.cnblogs.com/luckyblock/p/17705376.html

相关文章

  • 【面试题精讲】你了解String.intern方法吗
    有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top首发博客地址系列文章地址String.intern方法是Java中的一个方法,它用于将字符串对象添加到字符串常量池中,并返回常量池中该字符串的引用。如果常量池中已经存在该字符串,则......
  • LocalDate、LocalDateTime的用法与String互转
    一、LocalDate常用用法1.1、申明定义LocalDateformatDate=LocalDate.of(2020,2,5);//自定义LocalDatetoday=LocalDate.now();//获取当前日期1.2、getX()获取年月日等注意:获取月份使用getMonthValue()System.out.println(formatDate.getMonth());//FEBRUAR......
  • String与StringBuffer
    string与stringbuffer都是通过字符数组实现的。其中string的字符数组是final修饰的,所以字符数组不可以修改。stringbuffer的字符数组没有final修饰,所以字符数组可以修改。string与stringbuffer都是final修饰,只是限制他们所存储的引用地址不可修改。至于地址所指内容能不能修......
  • String、StringBuffer和StringBuilder的区别,ArrayList和linkedList的区别,HashMap和Has
    一、String、StringBuffer和StringBuilder的区别1.1相关介绍String是只读字符串,并不是基本数据类型,而是一个对象。从底层源码来看是一个final修饰的字符数组,所引用的字符串不能改变,一经定义无法再增删改。每次对String操作都会生成新的String对象。所以对于经常改变内容的字符串最......
  • PostCSS received undefined instead of CSS string
    问题npmrunserve启动项目后,报错SyntaxError:Error:PostCSSreceivedundefinedinsteadofCSSstring解决node-sass版本兼容问题导致,按照应用使用的node-sass版本切换(可使用nvm)到对应的node版本,再重新npmi......
  • Working With Strings In Python.
    #字符串操作在Python中,`string`是一种不可变的数据类型,用于表示文本或字符序列,可以使用单引号或双引号将字符串括起来。<fontcolor="#C7EDCC">所有修改和生成字符串的操作的实现方法都是另一个内存片段中新生成一个字符串对象。</font>##创建字符串```pystr1="Lefti......
  • How to fix Node.js fs.readFileSync toString Error All In One
    HowtofixNode.jsfs.readFileSynctoStringErrorAllInOneSyntaxError:UnexpectedendofJSONinput❌errorfs.writeFile&fs.readFileSync匹配错误asyncappendFile(group){console.log(`append`)constfile=path.join(__dirname+`/vide......
  • Python数据类型之字符串(String)
    Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。Python中常用的数据类型有6种,分别是:数字(Number)、字符串(String)、列表(List)、元组(Tuple)、字典(Dictionary)、集合(Set)。字符串(String)Python中的字符串用单引号''或者双引号""括起......
  • java.lang.ClassCastException: java.sql.Timestamp cannot be cast to java.lang.Str
    这个问题来自于想把从数据库查询的数据转化为字符串,方便后面做时间比较,显示格式转化错误 sql改造部分 as的左边为我的sql语句语法使用如下DATE_FORMAT((sql语句),'%Y-%m-%d%H:%i:%s')如果是涉及时间的计算,可以考虑如下方式BigDecimala=(BigDecimal)sprint......
  • C++的String与UF8互转
    UTF8_To_String#include<Stringapiset.h>#include<iostream>std::stringUTF8_To_String(conststd::string&str){intnwLen=MultiByteToWideChar(CP_UTF8,0,str.c_str(),-1,NULL,0);wchar_t*pwBuf=newwchar_t[nwLen+1];//一定要加......