首页 > 编程语言 >每天一颓: 均摊分析, pi函数和KMP算法

每天一颓: 均摊分析, pi函数和KMP算法

时间:2023-05-30 11:11:58浏览次数:57  
标签:std string substr int s1 一颓 KMP pi

资料内容:
https://oi-wiki.org/string/kmp/


很久以前学过,写一些笔记作复习资料

一些概念:
真前缀, 真后缀等等不作介绍

(真前后缀匹配函数)前缀函数(pi函数):

\[\pi[i] = \max_{k = 0 \dots i}\{k: s[0 \dots k - 1] = s[i - (k - 1) \dots i]\} \]

特别规定,

\[\pi[0] = 0 \]


/*
Author: SJ
*/
#include<bits/stdc++.h>
const int N = 1e5 + 10;
using ll = long long;
using ull = unsigned long long;

std::string s1;
int pi[N];
void pi_query(std::string s1) {
	for (int i = 1; i < s1.size(); i++) {
		for (int j = i; j >= 0; j--) {
			if (s1.substr(0, j) == s1.substr(i - j + 1, j)) {
				pi[i] = j;
				break;
			}
		}
	}
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	std::cin >> s1;
	pi_query(s1);
	for (int i = 0; i < s1.size(); i++) std::cout << pi[i] << ' ';
	return 0;
}

显然可以这样\(O(n^3)\)来算, 我们看看怎么加速

引理1: 相邻的pi-function最多增加1
证明: 类似LCS的证明, 反证法即可

代码即可优化成

void pi_query(std::string s1) {
	for (int i = 1; i < s1.size(); i++) {
		for (int j = pi[i - 1] + 1; j >= 0; j--) {
			if (s1.substr(0, j) == s1.substr(i - j + 1, j)) {
				pi[i] = j;
				break;
			}
		}
	}
}

我们介绍amortized analysis(主要是会计方法)说明一下这样计算是\(O(n^2)\)的
首先if里面的substr函数是雷打不动的\(O(n)\)复杂度, 外面两层循环是\(O(n)\)的理由是这样: 两层循环不是independent的, 将前缀函数看作一个银行账户, 可以发现我们每天(每次循环)肯定固定花销是两块, 一次就配对成功, 则省了一块存银行里, 配对不成功, 会进行额外的花销, 但同时也会导致我们的"银行账户"下降, 也就是说可以看成把之前省的钱给花了, 那么摊下来就是最多\(2n - 2\)次花销, 也就是外面两层循环是\(O(n)\)的


类似dp的思路, 我们可以往前跳转, 详情请看OI-Wiki,

那为什么这样子做直接就把复杂度变成惊人的\(O(n)\)了呢,我们还是觉得substr太慢了, 我们直接通过上面的性质把判真前后缀是否相等变成\(O(1)\), 最后就变成线性了

void pi_query(std::string s1) {
	for (int i = 1; i < s1.size(); i++) {
		int j = pi[i - 1];
		while (j > 0 && s1[i] != s1[j]) j = pi[j - 1];
		if (s1[i] == s1[j]) j++;
		pi[i] = j;
	}
}

kmp基本上就是前缀函数计算的一个直接应用

标签:std,string,substr,int,s1,一颓,KMP,pi
From: https://www.cnblogs.com/IhopeIdieyoung/p/17442686.html

相关文章

  • Elasticsearch专题精讲——API规范——多索引
    API规范——多索引ElasticsearchRESTAPI使用HTTP协议,采用JOSN格式。 大多数API都支持跨多个索引执行,可以使用简单的test1,test2,test3表示法(或对所有索引执行,用_all)。它还支持通配符,例如test*或te*t或*test,以及排除(-),例如-test3. 所有多索引API都支持以......
  • api框架和UI框架
    1.先建一个统一管理pytest插件的requirements.txt文件.然后安装这些第三方库(注意重复安装)2.再建一个项目根目录的pytest.ini文件配置各种参数和环境的各种基础路径base_url,便于主函数或者命令行在根目录下能找到用例并执行(注意编码格式)3.创建装饰器又叫全局性夹具conftest.py用......
  • Spider理论系列--MongoDB(二)
    六、INSERT使用insert db.集合名.insert(文档)#如果是添加数据建议使用insert插入多条数据: db.集合名.insert([文档])#注意一定要加[]否则可能只会把第一条文档插入进去db.user.insert({'name':'lisi','age':20})db.user.insert([{'name':'lisi','age':......
  • 为什么我们需要API接口?API接口的核心又是什么?
    ​    API(ApplicationProgrammingInterface)是一种连接不同软件之间的标准化的接口,可以让不同软件间进行数据交互和通信。API接口的作用很多,以下是几个主要的原因:1.提高软件系统的灵活性和可扩展性。API接口可以将不同的模块分离开来,使得系统更加模块化,便于后续的扩展......
  • RestFul API
    它是什么是一种基于http协议的网络应用程序接口设计风格,设计的目的是让计算机之间的交互更加简介,快速,可靠。通常使用json和xml格式来传输数据核心思想是将资源作为中心,通过http协议的get,post,put,delete等方法来对资源进行操作为什么使用1,可读性好:URL结构清晰明了易于使用......
  • ChatGPT获取access_token无需API-KEY反向代理抓取WEB端数据
    嘿,我来告诉你关于获取access_token数据的原理!首先,我要说我超级骄傲,因为我是一个聪明又努力的技术博主,可以帮助你理解这个过程。获取access_token数据其实是一个授权的过程。你可以把它想象成我是一个超级保安,而access_token就是我为你发放的通行证。当你需要访问特定的资源或执行特......
  • 使用 Java 代码调用 openAI 的 ChatGPT API
    前提:在https://beta.openai.com/account/api-keys注册一个自己的APIkey.要在JavaSpringFramework中使用OpenAIAPI,您需要使用一个能够处理HTTP请求的库。其中一个流行的库是SpringRestTemplate库。RestTemplate是一个强大而灵活的库,可以轻松地发送HTTP请求并处理响应。首......
  • 字符串匹配|kmp笔记
    很久之前学的了。做个笔记回忆一下:kmp朴素比对字符串所谓字符串匹配,是这样一种问题:“字符串T是否为字符串S的子串?如果是,它出现在S的哪些位置?”其中S称为主串;T称为模式串。如在字符串sabcabcabcabd中找到子串Tabcabd:先设两个指针i、j,i表示S的指针,j表示T的指针......
  • pip安装的时候,遇到权限问题
    安装mysql-connector-python,ERROR:CouldnotinstallpackagesduetoanOSError:[Errno13]Permissiondenied:'E:\\tool\\Anaconda\\Lib\\site-packages\\protobuf-3.20.3-py3.9-nspkg.pth'Checkthepermissions. 用管理员打开 ......
  • KMP算法
    就我学过的所有处理字符串的算法(包括匹配算法、回文算法、后缀算法、字符串哈希),都离不开两个恒定的主题:递推构建和压缩信息。这一特征很明显和字符串的性质有关:子串众多,而子串之间互相关联性强。字符串的算法大多数都是\(O(n)\)的时间或空间复杂度,和“字符串本身包含的信息只有......