华为OD机试 - 增强的strstr - 滑动窗口(Python/JS/C/C++ 2024 E卷 200分)

2024-09-06 09:20

本文主要是介绍华为OD机试 - 增强的strstr - 滑动窗口(Python/JS/C/C++ 2024 E卷 200分),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

华为OD机试 2024E卷题库疯狂收录中,刷题点这里

专栏导读

本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

一、题目描述

C语言有一个库函数Q:char *strstr(const char *haystack, const char *needle),实现在字符串haystack中查找第一次出现字符串needle的位置,如果未找到则返回null。

现要求实现一个fsrstr的增强函数,可以使用带可选字符的字符串来模糊查询,与strstr一样返回首次查找到的字符串位置。
可选段使用中括号[]标识,表示该位置可以选取其中任意一个字符即可满足匹配条件。例如"a[bc]“表示可以匹配"ab"或"ac”。

注意目标字符串中可选段可能出现多个。

二、输入描述

与strstr函数Q一样,输入参数是两个字符指针,分别是源字符串和目标字符串。

三、输出描述

与strstr函数Q不同,返回的是源字符串中,匹配子字符串相对于源字符串地址的偏移(从0开始算),如果没有匹配返回-1。

补充说明: 源字符串中也不会出现[],目标字符串中[]是成对出现的且非空,且不会出现嵌套。
输入的字符串长度在[1, 100]之间。

四、测试用例

测试用例1:

1、输入

abcd
b[cd]

2、输出

1

3、说明

相当于是在源字符串中查找bc或者bd,bc子字符串相对于abcd的偏移是1。

测试用例2:

1、输入

abcdefg
h[ij]

2、输出

-1

3、说明

目标字符串 tar 是 h[ij],表示匹配以字符 ‘h’ 开头,接着是字符 ‘i’ 或 ‘j’ 的字符串。

在源字符串 “abcdefg” 中,没有子字符串以字符 ‘h’ 开头。

滑动窗口从源字符串的第一个字符开始滑动,检查长度为 2 的子串,未找到符合条件的子串。

因此,输出结果是 -1,表示目标字符串不在源字符串中。

测试用例3:

1、输入

xyzabc
yza[bc]

2、输出

1

3、说明

目标字符串 tar 是 yza[bc],表示匹配以字符 ‘y’、‘z’、‘a’ 开头,接着是字符 ‘b’ 或 ‘c’ 的字符串。

在源字符串 “xyzabc” 中,子字符串 “yzab” 从索引 1 开始。

滑动窗口从源字符串的第一个字符开始滑动,检查长度为 5 的子串:

  1. 第一个窗口子串 “xyzab”:不完全匹配目标模式,因为第二个字符 ‘x’ 不符合。
  2. 第二个窗口子串 “yzabc”:完全匹配目标模式。

因此,输出结果是 1。

五、解题思路

1、滑动窗口

滑动窗口(Sliding Window)是一种常见的算法技巧,主要用于在一个列表或数组中找到满足某种条件的子列表或子数组。其核心思想是使用两个指针(通常称为窗口的左端和右端)来定义一个子区间,随着指针的移动,这个区间会“滑动”或“扩展”,从而高效地遍历并处理列表或数组中的元素。

滑动窗口适用于那些要求在连续子数组、子字符串、子区间中进行高效查找、匹配或计算的场景。滑动窗口算法通过避免重复计算,将时间复杂度降低到 O(n) 的量级。

滑动窗口适用哪些场景?

滑动窗口算法适用于以下几类问题:

字符串匹配与查找:如查找一个字符串中的所有异位词、找出最长的无重复子串、实现正则表达式的匹配。

数组的子区间问题:如找到满足某种条件的连续子数组,最大/最小和子数组问题。

固定窗口大小的子数组问题:如在一个数组中查找固定长度的子数组,使其满足某些条件(最大、最小、和等)。

动态窗口调整问题:如找到数组中最短的子数组,其和大于等于给定值。

2、为什么采用滑动窗口?

在本题中,滑动窗口是一个自然的选择,因为我们需要在源字符串 (src) 中找到目标字符串 (tar) 的第一次匹配位置,并且目标字符串可能包含可选字符段([]内的字符)。

3、具体步骤:

  1. 解析目标字符串:
    • 首先,将目标字符串 tar 解析为多个字符层次(levels),每个层次是一个字符集合(HashSet)。
    • 普通字符直接放入一个单字符集合中。
    • 当遇到一个 [ 时,表示开始一个可选字符段,读取所有在 [] 中的字符,直到遇到 ] 结束,将这些字符放入一个集合中。
  2. 构建模式层次结构:
    • 遍历目标字符串 tar,构建一个字符集合的列表(levels)。这个列表的每个元素是一个字符集合,表示在源字符串 src 中这个位置可以匹配的字符。
    • 例如,对于目标字符串 b[cd],会被解析为 [[‘b’], [‘c’, ‘d’]],表示第一位必须是 ‘b’,第二位可以是 ‘c’ 或 ‘d’。
  3. 滑动窗口匹配:
    • 使用滑动窗口的方法在源字符串 src 中查找目标模式。
    • 滑动窗口的长度与目标模式的长度相同,逐步移动窗口并检查每个窗口的子字符串是否与目标模式匹配。
    • 对于每一个窗口位置,我们依次检查源字符串的字符是否在对应的目标字符集合中。
  4. 返回结果:
    • 如果找到匹配,则返回匹配的起始索引。
    • 如果遍历完整个源字符串仍未找到匹配,则返回 -1。

六、Python算法源码

def find_pattern_index(source_string, target_pattern):"""查找目标模式在源字符串中的起始索引。:param source_string: 源字符串:param target_pattern: 包含可选字符的目标模式:return: 目标模式首次匹配的起始索引,如果未找到则返回-1"""# 将目标模式解析为多个字符层次结构,每个层次存储可能匹配的字符集合pattern_levels = []# 当前字符集合用于存储[]中的字符current_set = set()is_within_brackets = False# 解析目标模式for current_char in target_pattern:if current_char == '[':is_within_brackets = True  # 开始读取可选字符段elif current_char == ']':is_within_brackets = False  # 结束读取可选字符段pattern_levels.append(current_set)current_set = set()  # 重置集合else:if is_within_brackets:current_set.add(current_char)  # 添加到当前可选字符集合else:single_char_set = {current_char}pattern_levels.append(single_char_set)  # 添加单个字符作为集合# 调用辅助方法查找匹配索引return find_first_match_index(source_string, pattern_levels)def find_first_match_index(source_string, pattern_levels):"""查找源字符串中首次匹配的索引:param source_string: 源字符串:param pattern_levels: 目标模式解析后的字符集合列表:return: 匹配的起始索引,如果未找到则返回-1"""# 遍历源字符串,使用滑动窗口法查找匹配for i in range(len(source_string) - len(pattern_levels) + 1):is_match_found = True# 检查每一个层次的字符是否匹配for j in range(len(pattern_levels)):if source_string[i + j] not in pattern_levels[j]:is_match_found = Falsebreak  # 如果字符不匹配,退出检查if is_match_found:return i  # 返回匹配的起始索引# 如果未找到匹配,返回-1return -1if __name__ == "__main__":# 使用input读取输入source_string = input("请输入源字符串:")target_pattern = input("请输入目标模式字符串:")# 输出匹配结果result = find_pattern_index(source_string, target_pattern)print(result)

七、JavaScript算法源码

// 主函数
function main() {const readline = require('readline');const rl = readline.createInterface({input: process.stdin,output: process.stdout});rl.question("请输入源字符串:", function(sourceString) {rl.question("请输入目标模式字符串:", function(targetPattern) {// 输出匹配结果const result = findPatternIndex(sourceString, targetPattern);console.log(result);rl.close();});});
}/*** 查找目标模式在源字符串中的起始索引。** @param {string} sourceString - 源字符串* @param {string} targetPattern - 包含可选字符的目标模式* @return {number} 目标模式首次匹配的起始索引,如果未找到则返回-1*/
function findPatternIndex(sourceString, targetPattern) {// 将目标模式解析为多个字符层次结构,每个层次存储可能匹配的字符集合const patternLevels = [];// 当前字符集合用于存储[]中的字符let currentSet = new Set();let isWithinBrackets = false;// 解析目标模式for (let i = 0; i < targetPattern.length; i++) {const currentChar = targetPattern[i];if (currentChar === '[') {isWithinBrackets = true;  // 开始读取可选字符段} else if (currentChar === ']') {isWithinBrackets = false;  // 结束读取可选字符段patternLevels.push(currentSet);currentSet = new Set();  // 重置集合} else {if (isWithinBrackets) {currentSet.add(currentChar);  // 添加到当前可选字符集合} else {const singleCharSet = new Set([currentChar]);patternLevels.push(singleCharSet);  // 添加单个字符作为集合}}}// 调用辅助方法查找匹配索引return findFirstMatchIndex(sourceString, patternLevels);
}/*** 查找源字符串中首次匹配的索引** @param {string} sourceString - 源字符串* @param {Array<Set>} patternLevels - 目标模式解析后的字符集合列表* @return {number} 匹配的起始索引,如果未找到则返回-1*/
function findFirstMatchIndex(sourceString, patternLevels) {// 遍历源字符串,使用滑动窗口法查找匹配for (let i = 0; i <= sourceString.length - patternLevels.length; i++) {let isMatchFound = true;// 检查每一个层次的字符是否匹配for (let j = 0; j < patternLevels.length; j++) {if (!patternLevels[j].has(sourceString[i + j])) {isMatchFound = false;break;  // 如果字符不匹配,退出检查}}if (isMatchFound) {return i;  // 返回匹配的起始索引}}// 如果未找到匹配,返回-1return -1;
}// 调用主函数
main();

八、C算法源码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>// 定义最大字符串长度
#define MAX_LEN 101// 函数声明
int findPatternIndex(const char* sourceString, const char* targetPattern);
int findFirstMatchIndex(const char* sourceString, char** patternLevels, int levelCount);
void freePatternLevels(char** patternLevels, int levelCount);int main() {// 定义源字符串和目标字符串char sourceString[MAX_LEN];char targetPattern[MAX_LEN];// 输入源字符串和目标字符串printf("请输入源字符串:");fgets(sourceString, MAX_LEN, stdin);sourceString[strcspn(sourceString, "\n")] = '\0';  // 去除换行符printf("请输入目标模式字符串:");fgets(targetPattern, MAX_LEN, stdin);targetPattern[strcspn(targetPattern, "\n")] = '\0';  // 去除换行符// 查找匹配结果int result = findPatternIndex(sourceString, targetPattern);printf("%d\n", result);return 0;
}/*** 查找目标模式在源字符串中的起始索引。** @param sourceString 源字符串* @param targetPattern 包含可选字符的目标模式* @return 目标模式首次匹配的起始索引,如果未找到则返回-1*/
int findPatternIndex(const char* sourceString, const char* targetPattern) {// 用于存储解析后的字符集合char* patternLevels[MAX_LEN];int levelCount = 0;char currentSet[MAX_LEN] = {0};int currentIndex = 0;bool isWithinBrackets = false;// 解析目标模式for (int i = 0; i < strlen(targetPattern); i++) {char currentChar = targetPattern[i];if (currentChar == '[') {isWithinBrackets = true;  // 开始读取可选字符段currentIndex = 0;  // 重置当前集合索引memset(currentSet, 0, sizeof(currentSet));  // 清空当前集合} else if (currentChar == ']') {isWithinBrackets = false;  // 结束读取可选字符段patternLevels[levelCount] = (char*)malloc((currentIndex + 1) * sizeof(char));strcpy(patternLevels[levelCount], currentSet);  // 保存当前集合levelCount++;} else {if (isWithinBrackets) {currentSet[currentIndex++] = currentChar;  // 添加到当前可选字符集合} else {patternLevels[levelCount] = (char*)malloc(2 * sizeof(char));patternLevels[levelCount][0] = currentChar;patternLevels[levelCount][1] = '\0';  // 添加单个字符作为集合levelCount++;}}}// 查找匹配索引int result = findFirstMatchIndex(sourceString, patternLevels, levelCount);// 释放动态分配的内存freePatternLevels(patternLevels, levelCount);return result;
}/*** 查找源字符串中首次匹配的索引** @param sourceString 源字符串* @param patternLevels 目标模式解析后的字符集合列表* @param levelCount 目标模式解析后的字符集合数量* @return 匹配的起始索引,如果未找到则返回-1*/
int findFirstMatchIndex(const char* sourceString, char** patternLevels, int levelCount) {int sourceLen = strlen(sourceString);// 遍历源字符串,使用滑动窗口法查找匹配for (int i = 0; i <= sourceLen - levelCount; i++) {bool isMatchFound = true;// 检查每一个层次的字符是否匹配for (int j = 0; j < levelCount; j++) {if (strchr(patternLevels[j], sourceString[i + j]) == NULL) {isMatchFound = false;break;  // 如果字符不匹配,退出检查}}if (isMatchFound) {return i;  // 返回匹配的起始索引}}// 如果未找到匹配,返回-1return -1;
}/*** 释放动态分配的内存** @param patternLevels 目标模式解析后的字符集合列表* @param levelCount 目标模式解析后的字符集合数量*/
void freePatternLevels(char** patternLevels, int levelCount) {for (int i = 0; i < levelCount; i++) {free(patternLevels[i]);}
}

九、C++算法源码

#include <iostream>
#include <vector>
#include <set>
#include <string>using namespace std;/*** 查找目标模式在源字符串中的起始索引。** @param sourceString 源字符串* @param targetPattern 包含可选字符的目标模式* @return 目标模式首次匹配的起始索引,如果未找到则返回-1*/
int findPatternIndex(const string& sourceString, const string& targetPattern) {// 用于存储解析后的字符集合vector<set<char>> patternLevels;set<char> currentSet;bool isWithinBrackets = false;// 解析目标模式for (char currentChar : targetPattern) {if (currentChar == '[') {isWithinBrackets = true;  // 开始读取可选字符段currentSet.clear();  // 清空当前集合} else if (currentChar == ']') {isWithinBrackets = false;  // 结束读取可选字符段patternLevels.push_back(currentSet);  // 保存当前集合} else {if (isWithinBrackets) {currentSet.insert(currentChar);  // 添加到当前可选字符集合} else {set<char> singleCharSet = { currentChar };patternLevels.push_back(singleCharSet);  // 添加单个字符作为集合}}}// 调用辅助方法查找匹配索引return findFirstMatchIndex(sourceString, patternLevels);
}/*** 查找源字符串中首次匹配的索引** @param sourceString 源字符串* @param patternLevels 目标模式解析后的字符集合列表* @return 匹配的起始索引,如果未找到则返回-1*/
int findFirstMatchIndex(const string& sourceString, const vector<set<char>>& patternLevels) {int sourceLen = sourceString.length();// 遍历源字符串,使用滑动窗口法查找匹配for (int i = 0; i <= sourceLen - patternLevels.size(); i++) {bool isMatchFound = true;// 检查每一个层次的字符是否匹配for (int j = 0; j < patternLevels.size(); j++) {if (patternLevels[j].find(sourceString[i + j]) == patternLevels[j].end()) {isMatchFound = false;break;  // 如果字符不匹配,退出检查}}if (isMatchFound) {return i;  // 返回匹配的起始索引}}// 如果未找到匹配,返回-1return -1;
}int main() {// 定义源字符串和目标字符串string sourceString;string targetPattern;// 输入源字符串和目标字符串cout << "请输入源字符串:" << endl;getline(cin, sourceString);cout << "请输入目标模式字符串:" << endl;getline(cin, targetPattern);// 查找匹配结果int result = findPatternIndex(sourceString, targetPattern);cout << result << endl;return 0;
}

🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2024 E卷 200分)

🏆本文收录于,华为OD机试真题(Python/JS/C/C++)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

在这里插入图片描述

这篇关于华为OD机试 - 增强的strstr - 滑动窗口(Python/JS/C/C++ 2024 E卷 200分)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1141623

相关文章

一文教你Python如何快速精准抓取网页数据

《一文教你Python如何快速精准抓取网页数据》这篇文章主要为大家详细介绍了如何利用Python实现快速精准抓取网页数据,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录1. 准备工作2. 基础爬虫实现3. 高级功能扩展3.1 抓取文章详情3.2 保存数据到文件4. 完整示例

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

基于Python打造一个智能单词管理神器

《基于Python打造一个智能单词管理神器》这篇文章主要为大家详细介绍了如何使用Python打造一个智能单词管理神器,从查询到导出的一站式解决,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 项目概述:为什么需要这个工具2. 环境搭建与快速入门2.1 环境要求2.2 首次运行配置3. 核心功能使用指

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

利用Python打造一个Excel记账模板

《利用Python打造一个Excel记账模板》这篇文章主要为大家详细介绍了如何使用Python打造一个超实用的Excel记账模板,可以帮助大家高效管理财务,迈向财富自由之路,感兴趣的小伙伴快跟随小编一... 目录设置预算百分比超支标红预警记账模板功能介绍基础记账预算管理可视化分析摸鱼时间理财法碎片时间利用财

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

python处理带有时区的日期和时间数据

《python处理带有时区的日期和时间数据》这篇文章主要为大家详细介绍了如何在Python中使用pytz库处理时区信息,包括获取当前UTC时间,转换为特定时区等,有需要的小伙伴可以参考一下... 目录时区基本信息python datetime使用timezonepandas处理时区数据知识延展时区基本信息

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1