华为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网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

Python进行JSON和Excel文件转换处理指南

《Python进行JSON和Excel文件转换处理指南》在数据交换与系统集成中,JSON与Excel是两种极为常见的数据格式,本文将介绍如何使用Python实现将JSON转换为格式化的Excel文件,... 目录将 jsON 导入为格式化 Excel将 Excel 导出为结构化 JSON处理嵌套 JSON:

Python操作PDF文档的主流库使用指南

《Python操作PDF文档的主流库使用指南》PDF因其跨平台、格式固定的特性成为文档交换的标准,然而,由于其复杂的内部结构,程序化操作PDF一直是个挑战,本文主要为大家整理了Python操作PD... 目录一、 基础操作1.PyPDF2 (及其继任者 pypdf)2.PyMuPDF / fitz3.Fre

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

python中列表应用和扩展性实用详解

《python中列表应用和扩展性实用详解》文章介绍了Python列表的核心特性:有序数据集合,用[]定义,元素类型可不同,支持迭代、循环、切片,可执行增删改查、排序、推导式及嵌套操作,是常用的数据处理... 目录1、列表定义2、格式3、列表是可迭代对象4、列表的常见操作总结1、列表定义是处理一组有序项目的

python运用requests模拟浏览器发送请求过程

《python运用requests模拟浏览器发送请求过程》模拟浏览器请求可选用requests处理静态内容,selenium应对动态页面,playwright支持高级自动化,设置代理和超时参数,根据需... 目录使用requests库模拟浏览器请求使用selenium自动化浏览器操作使用playwright

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

Python极速搭建局域网文件共享服务器完整指南

《Python极速搭建局域网文件共享服务器完整指南》在办公室或家庭局域网中快速共享文件时,许多人会选择第三方工具或云存储服务,但这些方案往往存在隐私泄露风险或需要复杂配置,下面我们就来看看如何使用Py... 目录一、android基础版:HTTP文件共享的魔法命令1. 一行代码启动HTTP服务器2. 关键参