字符串专题详解

news2024/12/23 4:49:45

目录

字符串hash进阶

KMP算法

next数组

KMP算法

KMP算法优化

字符串hash进阶

字符串hash是指将一个字符串S映射为一个整数,使得该整数可以尽可能唯一地代表字符串S。那么在一定程度上,如果两个字符串转换成的整数相等,就可以认为这两个字符串相同。

针对字符串hash可以采用下面的式子:

H[i]=H[i-1]*26+index\left ( str[i] \right )

其中str[i]表示字符串的i号位,index函数将A~Z转换为0~25。在这个转换方式中,虽然字符串与整数是一一对应的,但是由于没有进行适当处理,因此当字符串长度较长时,产生的整数会非常大,没办法用一般的数据类型保存。为了应对这种情况,只能舍弃一些“唯一性”,将产生的结果对一个整数mod取模。

H[i]=\left ( H[i-1]*26+index\left ( str[i] \right ) \right ) MOD mod

通过这种方式可以把字符串转换成范围上能接受的整数。但这又会产生另外的问题,也就是可能有多个字符串的hash值相同,导致冲突。不过幸运的是,在实践中发现,在int数据范围内,如果把进制设置为一个10^{7}级别的素数p(例如10000019),同时把mod设置为一个10^{9}级别的素数(例如1000000007),那么冲突的概率将会变得非常小,很难发生冲突。

例题:给出N个只有小写字母的字符串,求其中不同的字符串的个数。

对这个问题,如果只用字符串hash来做,那么只需要将N个字符串使用字符串hash函数转换为N个整数,然后将它们排序去重即可。

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
const int MOD=1000000007;
const int p=10000019;
vector<int> ans;
long long hashFunc(string str){
	long long H=0;
	for(int i=0;i<str.length();i++){
		H=(H*p+str[i]-'a')%MOD;
	}
	return H;
}
int main(){
	string str;
	while(getline(cin,str),str!="#"){
		long long id=hashFunc(str);
		ans.push_back(id);
	}
	sort(ans.begin(),ans.end());
	int count=0;
	for(int i=0;i<ans.size();i++){
		if(i==0||ans[i]!=ans[i-1]){
			count++;
		}
	}
	cout<<count<<endl;
	return 0;
}

接着考虑求解字符串的子串的hash值,也就是求解H[i…j].经过一系列推导可以得出hash函数如下

H[i\cdots j]=\left ( \left ( H[j]-H[i-1]\times p^{j-i+1} \right ) MOD mod+mod\right ) MOD mod

然后来看一个问题:输入两个长度均不超过1000的字符串,求他们的最长公共子串的长度。 

对这个问题,可以先分别对两个字符串求出hash值(同时记录对应的长度),然后找出两堆子串对应的hash值中相等的那些,便可以找出最大长度,时间复杂度为O\left ( n^{2}+m^{2} \right ),其中n和m分别为两个字符串的长度。

#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL MOD=1000000007;
const LL P=100000019;
const LL maxn=1010;
LL powP[maxn],H1[maxn]={0},H2[maxn]={0};
vector<pair<int,int> > pr1,pr2;
void init(int len){
	powP[0]=1;
	for(int i=1;i<=len;i++){
		powP[i]=(powP[i-1]*P)%MOD;
	}
}
void calH(LL H[],string &str){
	H[0]=str[0];
	for(int i=1;i<str.length();i++){
		H[i]=(H[i-1]*P+str[i])%MOD;
	}
}
int calSingleSubH(LL H[],int i,int j){
	if(i==0){
		return H[j];
	}
	return ((H[j]-H[i-j]*powP[j-i+1])%MOD+MOD)%MOD;
}
void calSubH(LL H[],int len,vector< pair<int,int> > &pr){
	for(int i=0;i<len;i++){
		for(int j=0;j<len;j++){
			int hashValue=calSingleSubH(H,i,j);
			pr.push_back(make_pair(hashValue,j-i+1));
		}
	}
}
int getMax(){
	int ans=0;
	for(int i=0;i<pr1.size();i++){
		for(int j=0;j<pr2.size();j++){
			if(pr1[i].first==pr2[j].first){
				ans=max(ans,pr1[i].second);
			}
		}
	}
	return ans;
}
int main(){
	string str1,str2;
	getline(cin,str1);
	getline(cin,str2);
	init(max(str1.length(),str2.length()));
	calH(H1,str1);
	calH(H2,str2);
	calSubH(H1,str1.length(),pr1);
	calSubH(H2,str2.length(),pr2);
	printf("ans=%d\n",getMax());
	return 0;
}

现在考虑解决最长回文子串问题,这里将用字符串hash+二分的思路去解决它,时间复杂度为O\left ( nlogn \right )

对一个给定的字符串str,可以先求出其字符串hash数组H1,然后再将str反转,求出反转字符串rstr的hash数组H2,接着分回文串的奇偶情况进行讨论。

(1)回文串的长度为奇数:枚举回文中心点i,二分子串的半径k,找到最大的使子串[i-k,i+k]是回文串的k。其中判断子串[i-k,i+k]是回文串等价于判断str的两个子串[i-k,i]与[i,i+k]是否是相反的串。而这等价于判断str的[i-k,i]子串与反转子串rstr的[len-1-(i+k),len-1-i]子串是否相同([a,b]在反转字符串中的位置为[len-1-b,len-1-a]),因此只需要判断H1[i-k…i]与H2[len-1-(i+k)…len-1-i]是否相等即可

(2)回文串的长度为偶数:枚举回文空隙点,令i表示空隙左边第一个元素的下标,二分字串的半径为k,找到最大的使子串[i-k+1,i+k]是回文串的k。其中判断子串[i-k+1,i+k]是回文串等价于判断str的两个子串[i-k+1,i]与[i+1,i+k]是否是相反的串。而这等价于判断str的[i-k+1,i]子串与反转字符串rstr的[len-1-(i+k),len-1-(i+1)]子串是否相同,因此只需要判断H1[i-k+1…i]与H2[len-1-(i+k)…len-1-(i+1)]是否相等即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL MOD=1000000007;
const LL P=10000019;
const LL maxn=200010;
LL powP[maxn],H1[maxn],H2[maxn];
void init(){
	powP[0]=1;
	for(int i=1;i<maxn;i++){
		powP[i]=(powP[i-1]*P)%MOD;
	}
}
void calH(LL H[],string &str){
	H[0]=str[0];
	for(int i=1;i<str.length();i++){
		H[i]=(H[i-1]*P+str[i])%MOD;
	}
}
int calSingleSubH(LL H[],int i,int j){
	if(i==0){
		return H[j];
	}
	return ((H[j]-H[i-1]*powP[j-i+1])%MOD+MOD)%MOD;
}
int binarySearch(int l,int r,int len,int i,int isEven){
	while(l<r){
		int mid=(l+r)/2;
		int H1L=i-mid+isEven,H1R=i;
		int H2L=len-1-(i+mid),H2R=len-1-(i+isEven);
		int hashL=calSingleSubH(H1,H1L,H1R);
		int hashR=calSingleSubH(H2,H2L,H2R);
		if(hashL!=hashR){
			r=mid;
		}
		else{
			l=mid+1;
		}
	}
	return l-1;
}
int main(){
	init();
	string str;
	getline(cin,str);
	calH(H1,str);
	reverse(str.begin(),str.end());
	calH(H2,str);
	int ans=0;
	//奇回文
	for(int i=0;i<str.length();i++){
		int maxLen=min(i,(int)str.length()-1-i)+1;
		int k=binarySearch(0,maxLen,str.length(),i,0);
		ans=max(ans,k*2+1);
	} 
	//偶回文
	for(int i=0;i<str.length();i++){
		int maxLen=min(i+1,(int)str.length()-1-i)+1;
		int k=binarySearch(0,maxLen,str.length(),i,1);
		ans=max(ans,k*2);
	} 
	cout<<ans<<endl;
	return 0;
}

KMP算法

主要解决的是字符串的匹配问题,如果给定两个字符串text和pattern,需要判断字符串pattern是否是字符串pattern是否是字符串text的子串。

next数组

next[i]表示使子串s[0……i]的前缀s[0……k]等于后缀s[i-k……i]的最大的k(前缀和后缀可以重叠,但不能是s[0……i]本身,next[i]就是所求最长相等前后缀中前缀最后一位的下标。求解next数组的过程其实就是模式串pattern自我匹配的过程。

void getNext(char s[],int len){
	int j=-1;
	next[0]=-1;
	for(int i=1;i<len;i++){
		while(j!=-1&&s[i]!=s[j+1]){
			j=next[j];
		}
		if(s[i]==s[j+1]){
			j++;
		}
		next[i]=j;
	}
}

KMP算法

KMP算法的一般思路:

(1)初始化j=-1,表示pattern当前已被匹配的最后位。

(2)让i遍历文本串text,对每个i,执行(3)(4)来试图匹配text[i]和pattern[j+1]。

(3)不断令j=next[j],直到j回退为-1,或是text[i]=pattern[j+1]成立。

(4)如果text[i]==pattern[j+1],则令j++。如果j达到m-1,说明pattern是text的子串,返回true。

//KMP算法,判断pattern是否是text的子串
bool KMP(char text[],char pattern[]){
	int n=strlen(text),m=strlen(pattern);
	getNext(pattern,m);
	int j=-1;
	for(int i=0;i<n;i++){
		while(j!=-1&&text[i]!=pattern[j+1]){
			j=next[j];
		}
		if(next[i]==pattern[j+1]){
			j++;
		}
		if(j==m-1){
			return true;
		}
	}
	return false;
} 

统计模式串pattern出现次数的KMP算法代码如下:

int KMP(char text[],char pattern[]){
	int n=strlen(text),m=strlen(pattern);
	getNext(pattern,m);
	int ans=0,j=-1;
	for(int i=0;i<n;i++){
		while(j!=-1&&text[i]!=pattern[j+1]){
			j=next[j];
		}
		if(text[i]==pattern[j+1]){
			j++;
		}
		if(j==m-1){	
			ans++;
		    j=next[j];//让j回退到next[j]继续匹配 
		}
	}
	return ans;
}

KMP算法优化

可以使用nextval数组进行优化,nextval[i]的含义可以理解为当模式串pattern的i+1位发生失配时,i应回退到的最佳位置。求解nextval数组的代码如下:

void getNextval(char s[],int len){
	int j=-1;
	nextval[0]=-1;
	for(int i=1;i<len;i++){
		while(j!=-1&&s[i]!=s[j+1]){
			j=nextval[j];
		}
		if(s[i]==s[j+1]){
			j++;
		}
		if(j==-1||s[i+1]!=s[j+1]){
			nextval[i]=j;
		}
		else{
			nextval[i]=nextval[j];
		}
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Typora—适用于 Mac 和 Win 系统的优秀 Markdown 文本编辑器

Typora 是一款适用于 Mac 和 Win 系统的优秀 Markdown 文本编辑器&#xff0c;它以其简洁易用的界面和强大的功能受到了众多用户的喜爱。 首先&#xff0c;Typora 的界面设计非常简洁直观&#xff0c;没有过多繁杂的菜单和按钮&#xff0c;让用户能够专注于写作本身。它采用实时…

【计算机毕业设计】235基于微信小程序点餐系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

抖音用户新作品监控助手,第一时间获取博主作品信息。

声明&#xff1a; 本文以教学为基准、本文提供的可操作性不得用于任何商业用途和违法违规场景。本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。包含关注&#xff0c;点赞等 抖音新作品监控助手系统是一个功能强大的…

华火新能源集成灶评测:创新与品质的融合

在厨房电器的不断推陈出新中&#xff0c;华火新能源集成灶以其独特的魅力进入了人们的视野。今天&#xff0c;我们就来深入评测这款备受关注的产品——华火新能源集成灶 一、华火新能源集成灶的创新与环保 首先&#xff0c;我们先来探讨新能源集成灶的整体表现。华火新能源集成…

Java健身私教服务师傅小程序APP源码(APP+小程序+公众号+H5)

强身健体&#xff0c;私人定制的健身之旅 &#x1f3cb;️ 引言&#xff1a;探索私人健身新纪元 在现代都市的快节奏生活中&#xff0c;越来越多的人开始注重身体健康和健身塑形。然而&#xff0c;传统的健身房模式可能无法满足每个人的个性化需求。这时&#xff0c;一款名为…

如何利用V-Ray优化渲染效果——渲染100邀请码【7788】

3dmax效果图云渲染--渲染100 以3ds Max2025、VR6.2、CR11.2等最新版本为基础&#xff0c;兼容fp、acescg等常用插件&#xff0c;同时UT滤镜等参数也得到了同步支持。 注册填邀清码[7788]领30元礼包和免费渲染券哦~ 第一步&#xff1a;初次渲染 打开场景并将V-Ray设置为当前渲染…

基于复旦微FM33FG065A+OSRAM RGBi车载氛围灯方案

应用场景&#xff1a; 汽车内饰照明&#xff1a;这种方案可以用于改善汽车内部的氛围&#xff0c;提升驾驶舒适度和乘客体验。可以通过调节灯光颜色和亮度来营造不同的氛围&#xff0c;比如温馨的暖色调或者清爽的冷色调。 个性化定制&#xff1a;汽车制造商或者车主可以根据个…

kubesphere踩过的坑,持续更新....

踩过的坑 The connection to the server lb.kubesphere.local:6443 was refused - did you specify the right host… 另一篇文档中 dashboard 安装 需要在浏览器中输入thisisunsafe,即可进入登录页面 ingress 安装的问题 问题描述&#xff1a; 安装后通过命令 kubectl g…

Python实现连连看11

(2)isOneCornerLink()函数 isOneCornerLink()函数判断图片是否是单拐点连通。判断是否是单拐点连通有两种方式,如图14所示。 图14 判断单拐点连通的两种方式 从图14中可以看出,判断两张图片是否是单拐点连通,实际上就是判断图中红点与绿点所在图片是否分别与这两张图是直…

电商平台数据的认知与深度理解

随着信息技术的迅猛发展&#xff0c;电商平台已成为现代社会商业活动的重要舞台。在这个舞台上&#xff0c;数据不仅是交易的记录&#xff0c;更是企业决策的依据、用户行为的镜子和市场变化的晴雨表。本文将从多个维度对电商平台数据进行全面且深入的认知和理解。 一、数据的…

深入理解MySQL字符集

一、字符集介绍 字符集&#xff08;Character Set&#xff09;是多个字符的集合&#xff0c;它规定了字符在计算机中的编码方式。以下是关于字符集的详细介绍&#xff1a; 1. 字符集的定义与作用 字符集是各种文字和符号的总称&#xff0c;包括各国家文字、标点符号、图形符号…

图像分割——U-Net论文介绍+代码(PyTorch)

0、概要 原理大致介绍了一下&#xff0c;后续会不断精进改的更加详细&#xff0c;然后就是代码可以对自己的数据集进行一个训练&#xff0c;还会不断完善&#xff0c;相应其他代码可以私信我。 一、论文内容总结 摘要&#xff1a;人们普遍认为&#xff0c;深度网络成功需要数…

HarmonyOS开发日记 :自定义节点,实现 UI 组件 动态创建、更新

引言 UI动态操作包含组件的动态创建、卸载、更新等相关操作。 通过组件预创建&#xff0c;可以满足开发者在非build生命周期中进行组件创建&#xff0c;创建后的组件可以进行属性设置、布局计算等操作。之后在页面加载时进行使用&#xff0c;可以极大提升页面响应速度。 UI …

中国500米分辨率年最大LAI数据集(2000-2020)

叶面积指数是生态系统的一个重要结构参数&#xff0c;用来反映植物叶面数量、冠层结构变化、植物群落生命活力及其环境效应&#xff0c;为植物冠层表面物质和能量交换的描述提供结构化的定量信息&#xff0c;并在生态系统碳积累、植被生产力和土壤、植物、大气间相互作用的能量…

[Qt的学习日常]--常用控件2

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、widget的…

如何使用视频文案提取帮手将手机上视频里的声音转成文字?

在自媒体短视频日益增加的时候不少自媒体创作者如何将视频转文字的需求日益增加。本次将给大家分享一款针对广大职场青年用户群体的视频转文字工具&#xff0c;旨在为用户提供高效、准确的视频转文字服务。 如何将手机上的视频转成文字呢 视频转文字工具具有转换速度快&#…

11.无代码爬虫八爪鱼采集器抓取网站信息的实操案例——选择目标网站、提取标题、发布时间、评论内容、作者昵称、点赞数量等字段

首先&#xff0c;多数情况下免费版本的功能&#xff0c;已经可以满足绝大多数采集需求&#xff0c;想了解八爪鱼采集器版本区别的详情&#xff0c;请访问这篇帖子&#xff1a; https://blog.csdn.net/cctv1123/article/details/139581468 八爪鱼采集器免费版和个人版、团队版下…

【SpringBoot】SpringBoot:打造现代化微服务架构

文章目录 引言微服务架构概述什么是微服务架构微服务的优势 使用SpringBoot构建微服务创建SpringBoot微服务项目示例&#xff1a;创建订单服务 配置数据库创建实体类和Repository创建服务层和控制器 微服务间通信使用RestTemplate进行同步通信示例&#xff1a;调用用户服务 使用…

hadoop/hive/DBeaver启动流程

hadoop 启动 cd到指定目录下 cd /opt/module/hadoop-3.3.0/sbin/启动文件 ./start-all.shjps一下&#xff0c;查看显示的内容 应该显示以下内容 NameNode SecondaryNameNode DataNode ResourceManager NodeManager如果缺少namenode&#xff0c;那么执行 rm -rf /tmp/hadoo…

数据可视化实验二:回归分析、判别分析与聚类分析

目录 一、使用回归分析方法分析某病毒是否与温度呈线性关系 1.1 代码实现 1.2 线性回归结果 1.3 相关系数验证 二、使用判别分析方法预测某病毒在一定的温度下是否可以存活&#xff0c;分别使用三种判别方法&#xff0c;包括Fish判别、贝叶斯判别、LDA 2.1 数据集展示&am…