华为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使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

定价129元!支持双频 Wi-Fi 5的华为AX1路由器发布

《定价129元!支持双频Wi-Fi5的华为AX1路由器发布》华为上周推出了其最新的入门级Wi-Fi5路由器——华为路由AX1,建议零售价129元,这款路由器配置如何?详细请看下文介... 华为 Wi-Fi 5 路由 AX1 已正式开售,新品支持双频 1200 兆、配有四个千兆网口、提供可视化智能诊断功能,建

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Python Jupyter Notebook导包报错问题及解决

《PythonJupyterNotebook导包报错问题及解决》在conda环境中安装包后,JupyterNotebook导入时出现ImportError,可能是由于包版本不对应或版本太高,解决方... 目录问题解决方法重新安装Jupyter NoteBook 更改Kernel总结问题在conda上安装了

Python如何计算两个不同类型列表的相似度

《Python如何计算两个不同类型列表的相似度》在编程中,经常需要比较两个列表的相似度,尤其是当这两个列表包含不同类型的元素时,下面小编就来讲讲如何使用Python计算两个不同类型列表的相似度吧... 目录摘要引言数字类型相似度欧几里得距离曼哈顿距离字符串类型相似度Levenshtein距离Jaccard相