剑指offer(专项突破)---字符串

总目录:剑指offer(专项突破)---目录-CSDN博客


1.字符串的基本知识

C语言中: 

函数名功能描述
strcpy(s1, s2)将字符串s2复制到字符串s1中,包括结束符'\0',要求s1有足够空间容纳s2的内容。
strncpy(s1, s2, n)s2中最多n个字符复制到s1中。若s2的长度小于n,则s1中剩余部分用'\0'填充;若s2长度大于等于n,则s1不会以'\0'结尾(需手动添加)。
strcat(s1, s2)将字符串s2连接到字符串s1的末尾,s1要有足够空间容纳连接后的内容,会自动添加结束符'\0'
strncat(s1, s2, n)s2中最多n个字符连接到s1的末尾,然后添加'\0's1要预留足够空间。
strcmp(s1, s2)比较s1s2两个字符串的大小,按照字典序进行比较。若s1小于s2返回负整数;若s1等于s2返回 0;若s1大于s2返回正整数。
strncmp(s1, s2, n)比较s1s2中前n个字符的大小,按照字典序比较,返回值规则同strcmp
strlen(s)计算字符串s的长度,不包括结束符'\0',返回字符串中字符的个数。
strchr(s, c)在字符串s中查找字符c第一次出现的位置,若找到返回指向该字符的指针,若没找到返回nullptr
strrchr(s, c)在字符串s中查找字符c最后一次出现的位置,若找到返回指向该字符的指针,若没找到返回nullptr
strstr(s1, s2)在字符串s1中查找字符串s2第一次出现的位置,若找到返回指向s2s1中起始位置的指针,若没找到返回nullptr

C++中: 

函数名功能描述
size()返回字符串中字符的个数。
length()获取字符串的长度,即字符个数。
empty()判断字符串是否为空,为空返回true,否则返回false
clear()清空字符串内容,使其长度变为 0。
push_back(c)在字符串末尾添加一个字符c
pop_back()删除字符串末尾的一个字符。
compare(s2)比较当前字符串和s2的大小,按照字典序比较,返回值规则类似strcmp函数(小于返回负整数,等于返回 0,大于返回正整数)。
substr(pos, n)从索引pos位置开始提取连续的n个字符,若n省略,则提取从pos开始到末尾的所有字符,返回提取出来的子字符串。
find(s2, pos)从索引pos位置开始查找字符串s2第一次出现的位置,若找到返回位置索引,若没找到返回std::string::npos(一个特殊的表示未找到的值),若pos省略,则从开头查找。
rfind(s2, pos)从索引pos位置开始查找字符串s2最后一次出现的位置,返回值规则同find函数,若pos省略,则从末尾往前查找。
replace(pos, n, s2)将从索引pos开始的n个字符替换成字符串s2,若n省略,则替换从pos开始到末尾的所有字符。

2.双指针

第2章用两个指针来定位一个子数组,其中一个指针指向数组的第1个数字,另一个指针指向数组的最后一个数字,那么两个指针之间所包含的就是一个子数组。

如果将字符串看成一个由字符组成的数组,那么也可以用两个指针来定位一个子字符串,其中一个指针指向字符串的第1个字符,另一个指针指向字符串的最后一个字符,两个指针之间所包含的就是一个子字符串。

LCR 014. 字符串的排列 - 力扣(LeetCode)

题解:滑动窗口

数组模拟哈希表 cnt1 统计字符串 s1 中每个字符出现的次数,然后遍历字符串 s2,维护一个窗口大小为 m 的滑动窗口。

数组模拟哈希表 cnt2 统计窗口内每个字符出现的次数,当 cnt1=cnt2 时,说明窗口内的字符及其个数与字符串 s1 相同,返回 true 即可。

否则,遍历结束后,返回 false

时间复杂度 (m+n×∣Σ∣),空间复杂度 O(∣Σ∣)。其中 m 和 n 分别为字符串 s1 和 s2 的长度;而 ∣Σ∣ 为字符集的大小,本题中∣Σ∣=26。

class Solution 
{
public:bool checkInclusion(string s1, string s2) {int m = s1.size(), n = s2.size();if (m > n)return false;vector<int> cnt1(26), cnt2(26);for (int i = 0; i < m; ++i) {++cnt1[s1[i] - 'a'];++cnt2[s2[i] - 'a'];}if (cnt1 == cnt2)return true;for (int i = m; i < n; ++i) {++cnt2[s2[i] - 'a'];--cnt2[s2[i - m] - 'a'];if (cnt1 == cnt2)return true;}return false;}
};

优化:

每次加入和移除一个字符时,都需要比较两个哈希表,时间复杂度较高。我们可以维护一个变量 k,表示两个大小为 m 的字符串中,有多少种字符出现的个数不同。当 k=0 时,说明两个字符串中的字符个数相同。

时间复杂度 O(m+n+∣Σ∣),空间复杂度 O(∣Σ∣)。其中 m 和 n 分别为字符串 s1 和 s2 的长度;而 ∣Σ∣ 为字符集的大小,本题中 ∣Σ∣=26。

class Solution 
{
public:bool checkInclusion(string s1, string s2) {int m = s1.size(), n = s2.size();if (m > n)return false;vector<int> cnt(26);for (int i = 0; i < m; ++i) {--cnt[s1[i] - 'a'];++cnt[s2[i] - 'a'];}int k = 0;for (int x : cnt)if (x != 0)++ k;if (k == 0)return true;for (int i = m; i < n; ++i) {int a = s2[i - m] - 'a';int b = s2[i] - 'a';if (cnt[a] == 0)++ k;-- cnt[a];if (cnt[a] == 0)-- k;if (cnt[b] == 0)++ k;++ cnt[b];if (cnt[b] == 0)-- k;if (k == 0)return true;}return false;}
};

LCR 015. 找到字符串中所有字母异位词 - 力扣(LeetCode)

题解:滑动窗口

同LCR 014,优化方式也一样,添加一个差异计数器 

class Solution 
{
public:vector<int> findAnagrams(string s, string p) {int m = s.size();int n = p.size();vector<int> ans;if (m < n)return ans;vector<int> cnt1(26), cnt2(26);for (int i = 0; i < n; ++i) {++cnt1[s[i] - 'a'];++cnt2[p[i] - 'a'];}if (cnt1 == cnt2)ans.push_back(0);for (int i = n; i < m; ++i) {++cnt1[s[i] - 'a'];--cnt1[s[i - n] - 'a'];if (cnt1 == cnt2)ans.push_back(i - n + 1);}return ans;}
};

LCR 016. 无重复字符的最长子串 - 力扣(LeetCode)

题解:双指针 + 哈希表

遍历字符串 s,对于当前遍历到的字符 s[r],如果 s[r] 在 [l,r) 范围内有与 s[r] 相同的字符,我们就不断地向右移动指针 l,直到 ss[s[r]] 为 false,此时 [l,r) 中没有任何与 s[r] 相同的字符,我们就找到了以字符 s[r] 为结尾的最长子串。更新最长子串的长度,最终返回答案。

时间复杂度 O(n),空间复杂度 O(∣Σ∣),其中 n 为字符串 s 的长度,而 Σ 表示字符集,本题中字符集为所有 ASCII 码在 [0,128) 内的字符,即∣Σ∣=128。

class Solution 
{
public:int lengthOfLongestSubstring(string s) {bool ss[128] = {false};int ans = 0;for (int l = 0, r = 0; r < s.size(); ++ r) {while (ss[s[r]])ss[s[l++]] = false;ss[s[r]] = true;ans = max(ans, r - l + 1);}return ans;}
};

LCR 017. 最小覆盖子串 - 力扣(LeetCode)

题解:滑动窗口

  • 外层while循环 - 窗口扩张
    • while(r < s.size()):这个循环的条件是只要r指针还没遍历完整个字符串s,就持续向右移动r指针来扩展窗口,模拟窗口不断向右滑动去尝试包含t中所有字符的过程。
    • char i = s[r++];:每次获取r指针指向的字符,并将r指针后移一位来扩大窗口范围。
    • if(++curHash[i] <= baseHash[i]):将该字符在curHash数组中的出现次数加 1,然后判断加 1 后的次数是否小于等于其在baseHash数组中记录的t里该字符出现的次数。若成立,则将计数器count加 1。
  • 内层while循环 - 窗口收缩
    • while(count == t.size()):当count的值等于字符串t的长度时,意味着当前窗口已经包含了t中的所有字符,此时就进入内层循环来尝试收缩窗口,看能否找到更小的符合条件的窗口。
    • if(r-l < minLen):判断当前窗口的长度(r - l)是否小于已记录的最小窗口长度minLen,如果是,则更新k为当前窗口的起始位置l,更新minLen为当前窗口的长度,即找到了一个更短的包含t所有字符的窗口。
    • char o = s[l++];:从窗口的左边开始收缩,获取要移出窗口的字符,并将l指针后移一位。
    • if(curHash[o]-- <= baseHash[o]):将该字符在curHash数组中的出现次数减 1,然后判断减 1 后的次数是否小于等于其在baseHash数组中记录的t里该字符出现的次数。若成立,则将计数器count减 1。

两个指针 l 和 r 都是从最左端向最右端移动,且 l 的位置一定在r 的左边或重合。注意本题虽然在 while 循环里出现了一个 while 循环,但是因为内循环负责移动 l 指针,且 l 只会从左到右移动一次,因此总时间复杂度仍然是 O(n)。 

class Solution
{
public:string minWindow(string s, string t){//先统计字符情况int baseHash[128] = { 0 }, curHash[128] = { 0 };for(auto& ch : t){++baseHash[ch];}int k = 0, minLen = s.size() + 1;//k:记录窗口起始位置 minLen:记录最小窗口长度int l = 0, r = 0, count = 0;//外层循环:窗口扩张while(r < s.size()){char i = s[r++];if(++curHash[i] <= baseHash[i]){++count;}//内层循环:窗口收缩while(count == t.size()){if(r-l < minLen){k = l;minLen = r-l;}char o = s[l++];if(curHash[o]-- <= baseHash[o]){--count;}}}return minLen > s.size() ? "" : s.substr(k, minLen);}
};

3.回文字符串

LCR 018. 验证回文串 - 力扣(LeetCode)

题解:双指针

时间复杂度 O(n)。空间复杂度 O(1)。 

class Solution 
{
public:bool isPalindrome(string s) {int l = 0, r = s.size() - 1;while (l < r) {while (l < r && !isalnum(s[l]))++ l;while (l < r && !isalnum(s[r]))-- r;if (tolower(s[l]) != tolower(s[r]))return false;++ l;-- r;}return true;}
};

LCR 019. 验证回文串 II - 力扣(LeetCode)

题解:双指针 + 递归

时间复杂度 O(n)。空间复杂度 O(1)。 

class Solution 
{
public:bool validPalindrome(string s) {auto check = [&](int i, int j) {for (; i < j; ++i, --j)if (s[i] != s[j])return false;return true;};for (int i = 0, j = s.size() - 1; i < j; ++i, --j) if (s[i] != s[j])return check(i + 1, j) || check(i, j - 1);return true;}
};

LCR 020. 回文子串 - 力扣(LeetCode)

题解1:从中心向两侧扩展回文串

外层循环:遍历字符串每一位

内层循环:分别计算当前字符为中心点当前字符与下一位字符为中心点 

时间复杂度 O(n^2)。

class Solution 
{
public:int countSubstrings(string s) {int ans = 0;auto f = [&](int i, int j) -> int {int cnt = 0;for (; i >= 0 && j < s.size() && s[i] == s[j]; -- i, ++ j)++cnt;return cnt;};for (int i = 0; i < s.size(); ++ i)ans += f(i, i) + f(i, i + 1);return ans;}
};

题解2:Manacher 算法 

在 Manacher 算法的计算过程中,用 p[i]−1 表示以第 i 位为中心的最大回文长度,以第 i 位为中心的回文串数量为 \left \lceil \frac{p[i] - 1}{2} \right \rceil

时间复杂度 O(n),空间复杂度 O(n)。

class Solution 
{
public:int countSubstrings(string s) {int n = s.size();string t = "!#";for (const char &c: s) {t += c;t += '#';}n = t.size();t += '$';auto f = vector <int> (n);int mid = 0, rMax = 0, ans = 0;for (int i = 1; i < n; ++i) {// 初始化 f[i]f[i] = (i <= rMax) ? min(rMax - i + 1, f[2 * mid - i]) : 1;// 中心拓展while (t[i + f[i]] == t[i - f[i]]) ++f[i];if (i + f[i] - 1 > rMax) {mid = i;rMax = i + f[i] - 1;}// 当前贡献为 (f[i] - 1) / 2 上取整ans += (f[i] / 2);}return ans;}
};

4.小结

变位词和回文是很有意思的文字游戏。如果两个字符串包含的字符及每个字符出现的次数都相同,只是字符出现的顺序不同,那么它们就是一组变位词。通常可以用一个哈希表来统计每个字符出现的次数,有了哈希表就很容易判断两个字符串是不是一组变位词。

回文是一类特殊的字符串。不管是从前往后还是从后往前读取其每一个字符,得到的内容都是一样的。通常可以用两个指针来判断一个字符串是不是回文,要么两个指针从字符串的两端开始向中间移动,要么两个指针从中间开始向两端移动。

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

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

相关文章

ORB-SLAM2源码学习:MapPoint.cc:MapPoint::UpdateNormalAndDepth()计算平均观测方向以及观测距离范围1

前言 这个函数是属于地图点属性的一部分。 1.函数声明 void MapPoint::UpdateNormalAndDepth() {.... } 2.函数定义 1.获取观测到该地图点的所有关键帧的信息 map<KeyFrame*,size_t> observations;KeyFrame* pRefKF;cv::Mat Pos;{unique_lock<mutex> lock1(…

【JVM什么时候触发YoungGC和FullGC】

YoungGC 年轻代Eden区满&#xff0c;就会触发YoungGC FullGC 老年代空间不足 经过多次GC后的大年龄对象会被放进老年代&#xff0c;或创建的大对象会直接在老年代分配&#xff0c;此时若老年代空间不足&#xff0c;就会触发FullGC。空间分配担保失败 触发YoungGC的时候会进行…

用“*”构成一个倒三角形:JAVA

输入&#xff1a;5 输出&#xff1a; ******* ***** *** * 代码&#xff1a; import java.util.Scanner; //倒三角 public class FF6 {public static void main(String[] args) {Scanner scannernew Scanner(System.in);while (scanner.hasNextInt()){int nscanner…

充分统计量(Sufficient Statistic)概念与应用: 中英双语

充分统计量&#xff1a;概念与应用 在统计学中&#xff0c;充分统计量&#xff08;Sufficient Statistic&#xff09; 是一个核心概念。它是从样本中计算得出的函数&#xff0c;能够完整且无损地表征样本中与分布参数相关的信息。在参数估计中&#xff0c;充分统计量能够帮助我…

ubuntu环境下安装electron环境,并快速打包

1.配置镜像源 关闭防火墙&#xff0c;命令&#xff1a;sudo ufw disable 1.1配置国内镜像源&#xff1a; vim /etc/apt/source.list deb https://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiversedeb-src https://mirrors.aliyun.com/ubuntu/ jammy main…

【eclipse】快捷键

【eclipse】快捷键 编辑导航重构调试复制其他快速生成 Eclipse 提供了丰富的快捷键来帮助开发者提高工作效率。 以下是一些常用的 Eclipse 快捷键&#xff0c;它们覆盖了编辑、导航、重构、调试等多个方面。 这些快捷键能够显著提升开发效率&#xff0c;尤其是在处理大型项目时…

yarn : 无法加载文件 E:\node\node_global\yarn.ps1,因为在此系统上禁止运行脚本

先确保安装了yarn —— npm install -g yarn 终端输入set-ExecutionPolicy RemoteSigned 若要在本地计算机上运行您编写的未签名脚本和来自其他用户的签名脚本&#xff0c;请使用以下命令将计算机上的执行策略更改为RemoteSigned 再去使用yarn okk~

python学opencv|读取视频(二)制作gif

【1】引言 前述已经完成了图像和视频的读取学习&#xff0c;本次课学习制作gif格式动图。 【2】教程 实际上想制作gif格式动图是一个顺理成章的操作&#xff0c;完成了图像和视频的处理&#xff0c;那就自然而然会对gif的处理也产生兴趣。 不过在opencv官网、matplotlib官网…

高校心理教育辅导|基于springBoot的高校心理教育辅导系统设计与实现(附项目源码+论文+数据库)

私信或留言即免费送开题报告和任务书&#xff08;可指定任意题目&#xff09; 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取 一、摘要 随着Internet技术的发展&#xff0c;心理教育辅导系统应运而生&…

dreamerv3复现教程

dreamerv3复现教程 &#xff11;、dreamerv3基础安装&#xff12;、gym0.19.0安装3、JDK8安装&#xff14;、mineRL&#xff10;.4.4安装&#xff15;、diamond_env安装 由于dreamerv3存在很多版本冲突问题&#xff0c;所以很难直接使用和复现 &#xff11;、dreamerv3基础安装…

Mock 对象 详解

Mock 对象详解 Mock 对象 是软件测试中常用的技术&#xff0c;用于模拟实际对象的行为&#xff0c;以便隔离待测试的代码&#xff0c;与其他依赖解耦&#xff0c;进行单元测试。通过 Mock 对象&#xff0c;我们可以控制依赖对象的行为&#xff0c;使得测试更专注于被测单元的功…

如何参加华为欧拉考试?

华为欧拉考试主要针对的是华为欧拉&#xff08;EulerOS/openEuler&#xff09;操作系统的认证考试&#xff0c;这一认证体系旨在培养和认证具备基于欧拉操作系统进行企业级应用运行基础环境搭建、管理和调测能力的工程师以及云计算架构师。以下是对华为欧拉考试的详细介绍&…

网络安全、Web安全、渗透测试之笔经面经总结(一)

本篇文章总结涉及以下几个方面&#xff1a; 一&#xff1a;对称加密非对称加密&#xff1f; 对称加密&#xff1a;加解密用同一密钥&#xff0c;密钥维护复杂n&#xff08;n-1&#xff09;/2&#xff0c;不适合互联网传输密钥&#xff0c;加解密效率高。应用于加密数据。 非…

Nginx静态资源配置

基本配置原则 明确资源目录&#xff1a;为不同类型的静态资源指定不同的路径&#xff0c;这样可以避免路径冲突&#xff0c;并且便于管理。正确设置文件权限&#xff1a;确保 Nginx 具有读取静态资源的权限。缓存优化&#xff1a;为静态资源设置缓存头&#xff08;如 expires&…

Influxdb 基础操作

InfluxDB是一个由InfluxData开发的开源时序数据库&#xff0c;专为高性能地查询与存储时序数据而设计。以下是对InfluxDB基础操作的介绍&#xff1a; 一、数据库操作 创建数据库 使用CREATE DATABASE语句可以创建一个新的数据库。例如&#xff0c;创建一个名为example_db的数…

bert-base-uncased模型的tokenizer.json和vocab.txt的区别和联系

说在前面 突然发现这两个文件是由区别的&#xff0c;之前傻傻的把两个文件都下载到模型里面了&#xff0c;transformers工具包使用bert加载分词器时并不是都需要这两个文件的&#xff0c;而是工具加载器类型类具体决定的。 vocab.txt 的作用 词汇表文件&#xff0c;通常是一…

华为NPU服务器昇腾Ascend 910B2部署通义千问Qwen2.5——基于mindie镜像一路试错版(三)

文章目录 前言纯模型推理启动服务后面干什么?这可咋整啊?愁死了!总结前言 这是咱这个系列的第三个文章了。 毕竟,这是我好几天摸索出的经验,能帮助各位在几个小时内领会,我觉得也算是我的功劳一件了。 所以,一是希望大家耐心看下去,耐心操作下去;而是恳请各位多多关…

linux静态链接和动态链接

静态链接的特点 程序独立性高 静态链接是在程序编译时&#xff0c;将所有需要的目标文件以及它们所依赖的库文件中的代码和数据链接成一个可执行文件。一旦链接完成&#xff0c;这个可执行文件就包含了运行所需的全部内容&#xff0c;不依赖外部的库文件。例如&#xff0c;一个…

Android学习8 -- NDK2--练习2(Opencv)

以下是一个简单的安卓项目示例&#xff0c;通过NDK调用OpenCV来处理图像&#xff08;例如&#xff0c;将彩色图像转换为灰度图像&#xff09;。 开发环境 安装 Android Studio&#xff08;支持NDK开发&#xff09;。配置NDK和CMake&#xff08;通过Android Studio的SDK Manage…

Pixtral Large开源:Mistral AI的1240亿参数多模态模型超越GPT-4o等竞争对手

Pixtral Large是什么 Pixtral Large是由法国人工智能初创公司Mistral AI开发的超大多模态模型&#xff0c;拥有1240亿参数&#xff0c;2024年11月18日正式对外发布。它基于Mistral Large 2开发而成&#xff0c;具备1230亿参数的多模态解码器和10亿参数的视觉编码器。这个模型能…