首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【华为OD-E卷 -64 关联子串 100分(python、java、c++、js、c)】

  • 25-03-07 19:22
  • 4792
  • 6360
blog.csdn.net

【华为OD-E卷 - 关联子串 100分(python、java、c++、js、c)】

题目

给定两个字符串str1和str2,如果字符串str1中的字符,经过排列组合后的字符串中,只要有一个字符串是str2的子串,则认为str1是str2的关联子串。
若str1是str2的关联子串,请返回子串在str2的起始位置;
若不是关联子串,则返回-1

输入描述

  • 输入两个字符串,分别为题目中描述的str1、str2

输出描述

  • 若str1是str2的关联子串,请返回子串在str2的起始位置;

若不是关联子串,则返回-1。

若str2中有多个str1的组合子串,请返回最小的起始位置

备注

  • 输入的字符串只包含小写字母; 两个字符串的长度范围[1, 100000]之间;

用例

用例一:
输入:
abc efghicbaiii
  • 1
输出:
5
  • 1
用例二:
输入:
abc efghiccaiii
  • 1
输出:
-1
  • 1

python解法

  • 解题思路:
  • 解题思路
    题目给定了两个字符串 str1 和 str2,要求判断 str1 是否是 str2 中的一个字母排列(即 str1 的字符可以通过重排后在 str2 中找到)。如果找到,则返回第一个符合的起始位置;如果没有找到,则返回 -1。

这个问题可以通过 滑动窗口 的方式高效解决。具体步骤如下:

长度检查:首先,如果 str1 的长度大于 str2 的长度,直接返回 -1,因为不可能有子串满足条件。

字符计数:使用两个计数数组 count1 和 count2 来分别记录 str1 和 str2 的字符频率。每个字符的频率存在一个长度为 26 的数组中(假设只包含小写字母)。对于 str1,我们记录其每个字符的频率;然后,我们用滑动窗口的方式遍历 str2,并在窗口中计算字符的频率。

滑动窗口:我们从 str2 的前 n 个字符(n 是 str1 的长度)开始,检查其字符频率是否与 str1 的字符频率相同。如果相同,则返回当前窗口的起始位置。然后,我们滑动窗口:每次窗口右移一个字符,更新窗口的字符频率,移除窗口最左边的字符并添加新的字符,继续比较两者的频率是否相同。如果相同,则返回窗口的起始位置。

结束条件:如果遍历完所有可能的窗口后都没有找到符合的子串,则返回 -1

str1, str2 = input().split()  # 输入两个字符串

# 定义一个函数来处理逻辑
def getResult():
    n, m = len(str1), len(str2)  # 获取两个字符串的长度
    if n > m:  # 如果 str1 长度大于 str2 长度,直接返回 -1
        return -1
    
    # 用于记录 str1 和 str2 中每个字符出现次数的数组
    count1 = [0] * 26  # 记录 str1 中每个字符的频率
    count2 = [0] * 26  # 记录 str2 中当前窗口中字符的频率
    
    # 计算 str1 中每个字符的频率
    for c in str1:
        count1[ord(c) - ord('a')] += 1  # 将字符 c 转换为对应的索引并增加计数
    
    # 初始化 str2 中的窗口,长度为 n
    for i in range(n):
        count2[ord(str2[i]) - ord('a')] += 1  # 统计窗口中字符的频率
    
    # 如果 str1 和 str2 的前 n 个字符频率一致,直接返回 0,表示从第 0 位开始
    if count1 == count2:
        return 0
    
    # 滑动窗口,从第 n 位开始,逐个字符滑动窗口
    for i in range(n, m):
        # 增加当前字符的计数
        count2[ord(str2[i]) - ord('a')] += 1
        # 移除窗口最左边的字符
        count2[ord(str2[i - n]) - ord('a')] -= 1
        
        # 如果此时的窗口字符频率与 str1 的频率一致,返回当前窗口的起始位置
        if count1 == count2:
            return i - n + 1
    
    # 如果遍历完所有窗口都没有找到匹配的子串,返回 -1
    return -1

# 调用函数并输出结果
print(getResult())

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

java解法

  • 解题思路
  • 解题思路
    这道题的目的是在给定的字符串 str2 中找到一个子串,该子串是字符串 str1 的字母排列之一(即 str1 中字符的重排)。如果找到,返回第一个符合条件的子串的起始索引;如果找不到,返回 -1。

为了解决这个问题,我们可以使用 滑动窗口 和 字符计数 的方法。具体步骤如下:

初始化字符计数:首先计算 str1 中每个字符的频率,存储在 targetCount 数组中。然后,使用一个滑动窗口遍历 str2,在窗口中计算字符的频率,存储在 windowCount 数组中。

初始窗口匹配:首先,检查 str2 的前 n 个字符(n 是 str1 的长度)是否与 str1 的字符频率匹配。如果匹配,返回起始索引 0。

滑动窗口:将窗口从 str2 的前 n 个字符滑动到后面的字符。每次滑动窗口时,更新 windowCount,即移除窗口最左边的字符并加入窗口右边的新字符。每次更新后,检查窗口中的字符频率是否与 targetCount 匹配,如果匹配,返回当前窗口的起始位置。

结束条件:如果遍历完所有窗口仍未找到匹配的子串,返回 -1。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String str1 = scanner.next();  // 读取第一个字符串
        String str2 = scanner.next();  // 读取第二个字符串
        System.out.println(findSubstr(str1, str2));  // 输出结果
    }

    // findSubstr 方法用于找到第一个符合条件的子串
    public static int findSubstr(String str1, String str2) {
        // 创建两个数组,分别记录 str1 和滑动窗口中字符的出现次数
        int[] targetCount = new int[26];  // str1 的字符频率数组
        int[] windowCount = new int[26];  // str2 中当前窗口的字符频率数组

        // 统计 str1 中每个字符的频率
        for (char c : str1.toCharArray()) {
            targetCount[c - 'a']++;  // 将字符转为对应的索引('a' -> 0, 'b' -> 1, ..., 'z' -> 25)
        }

        // 统计 str2 中前 n 个字符的频率,n 为 str1 的长度
        for (int i = 0; i < str1.length(); i++) {
            windowCount[str2.charAt(i) - 'a']++;  // 更新窗口字符频率
        }

        // 如果初始窗口中的字符频率已经匹配,直接返回 0(表示从第 0 位开始符合条件)
        if (matches(targetCount, windowCount)) {
            return 0;
        }

        // 从 str2 中第 n 个字符开始,滑动窗口
        for (int i = str1.length(); i < str2.length(); i++) {
            // 更新窗口,移除最左边的字符,添加新的字符
            windowCount[str2.charAt(i - str1.length()) - 'a']--;  // 移除最左边的字符
            windowCount[str2.charAt(i) - 'a']++;  // 添加新的字符

            // 检查更新后的窗口字符频率是否与 str1 的字符频率匹配
            if (matches(targetCount, windowCount)) {
                return i - str1.length() + 1;  // 返回窗口的起始位置
            }
        }

        // 如果遍历完所有窗口都没有找到匹配的子串,返回 -1
        return -1;
    }

    // matches 方法用于比较两个字符频率数组是否完全相同
    private static boolean matches(int[] targetCount, int[] windowCount) {
        for (int i = 0; i < 26; i++) {
            if (targetCount[i] != windowCount[i]) {  // 如果某个字符的频率不匹配
                return false;  // 返回 false
            }
        }
        return true;  // 所有字符频率匹配,返回 true
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

C++解法

  • 解题思路
  • 解题思路
    这道题的目的是在字符串 str2 中找到一个与 str1 字符排列相同的子串(即 str1 中字符的重排)。我们可以利用 滑动窗口 和 字符计数 技巧来高效地解决此问题。具体的思路如下:

字符计数:首先统计 str1 中每个字符的频率,存储在 targetCount 数组中。接着使用滑动窗口的方法遍历 str2,统计窗口中的字符频率,并存储在 windowCount 数组中。

初始窗口匹配:首先,检查 str2 的前 n 个字符(n 为 str1 的长度)是否与 str1 的字符频率匹配。如果匹配,直接返回起始索引 0。

滑动窗口:将窗口从 str2 的前 n 个字符滑动到后面的字符。每次滑动时,更新 windowCount 数组(即移除窗口最左边的字符并加入新字符)。每次更新后,检查窗口中的字符频率是否与 str1 的频率相匹配。如果匹配,返回当前窗口的起始位置。

结束条件:如果遍历完所有窗口仍未找到匹配的子串,则返回 -1。

#include 
#include 
#include 

using namespace std;

// findSubstr 函数用于查找字符串 str2 中第一个与 str1 字母排列相同的子串
int findSubstr(const string& str1, const string& str2) {
    // targetCount 数组记录 str1 中每个字符的频率
    vector<int> targetCount(26, 0);  
    // windowCount 数组记录当前滑动窗口中每个字符的频率
    vector<int> windowCount(26, 0);

    // 统计 str1 中每个字符的频率
    for (char c : str1) {
        targetCount[c - 'a']++;  // 字符 'a' 对应下标 0, 'b' 对应下标 1, ..., 'z' 对应下标 25
    }

    // 初始化滑动窗口,统计 str2 中前 str1.length() 个字符的频率
    for (int i = 0; i < str1.length(); i++) {
        windowCount[str2[i] - 'a']++;  // 更新窗口字符频率
    }

    // 如果初始窗口的字符频率已经匹配,直接返回 0
    if (targetCount == windowCount) {
        return 0;
    }

    // 从 str2 的第 str1.length() 个字符开始滑动窗口
    for (int i = str1.length(); i < str2.length(); i++) {
        // 更新窗口:移除窗口最左边的字符,加入新字符
        windowCount[str2[i - str1.length()] - 'a']--;  // 移除最左边字符
        windowCount[str2[i] - 'a']++;  // 添加新字符

        // 检查更新后的窗口字符频率是否与 str1 的字符频率匹配
        if (targetCount == windowCount) {
            return i - str1.length() + 1;  // 返回当前窗口的起始位置
        }
    }

    // 如果没有找到符合条件的子串,返回 -1
    return -1;
}

int main() {
    string str1, str2;
    cin >> str1 >> str2;  // 读取输入的两个字符串
    cout << findSubstr(str1, str2) << endl;  // 输出结果
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

C解法

  • 解题思路

  • 解题思路
    这道题要求我们在字符串 str2 中查找是否有一个与字符串 str1 字母排列相同的子串,并返回第一个符合条件子串的起始位置。我们可以通过 滑动窗口 和 字符计数 技巧来高效地实现该功能。

步骤解析:

字符计数:

我们将 str1 中的每个字符的频率存储在 count1 数组中。
同样,我们将 str2 中当前窗口的字符频率存储在 count2 数组中。
初始窗口:

初始化滑动窗口为 str2 中的前 len1 个字符(即 str1 的长度),并将其字符频率存储在 count2 中。
滑动窗口:

从 str2 的第一个字符开始,滑动窗口每次向右移动一个字符。每移动一步:
移除窗口最左边的字符(即 s2[i-1]),并添加新的字符(即 s2[i + len1 - 1])。
比较 count1 和 count2 是否相等,如果相等,说明当前窗口是 str1 的字母排列,返回当前窗口的起始位置。
结束条件:

如果找到了符合条件的子串,则返回其起始索引;如果遍历整个 str2 后都未找到,则返回 -1。

#include 
#include 

#define ALPHABET_SIZE 26  // 定义字母表大小,因为我们只考虑小写字母

// checkInclusion 函数用于检查 str2 中是否有子串是 str1 的字母排列
int checkInclusion(char* s1, char* s2) {
    int len1 = strlen(s1), len2 = strlen(s2);  // 获取两个字符串的长度
    if (len1 > len2) return -1;  // 如果 str1 的长度大于 str2,直接返回 -1

    int count1[ALPHABET_SIZE] = {0}, count2[ALPHABET_SIZE] = {0};  // 初始化两个字符计数数组
    // 统计 str1 和 str2 中前 len1 个字符的频率
    for (int i = 0; i < len1; i++) {
        count1[s1[i] - 'a']++;  // 将 str1 中每个字符的频率加入 count1
        count2[s2[i] - 'a']++;  // 将 str2 中前 len1 个字符的频率加入 count2
    }

    // 滑动窗口开始检查
    for (int i = 0; i <= len2 - len1; i++) {
        // 如果不是第一次循环,滑动窗口:更新 count2
        if (i > 0) {
            count2[s2[i - 1] - 'a']--;  // 移除窗口最左边的字符
            count2[s2[i + len1 - 1] - 'a']++;  // 添加新字符
        }
        // 比较当前窗口的字符频率是否与 str1 的字符频率相同
        if (memcmp(count1, count2, sizeof(count1)) == 0) {
            return i;  // 如果相同,返回当前窗口的起始位置
        }
    }

    return -1;  // 如果遍历完所有窗口仍未找到,返回 -1
}

int main() {
    char str1[100001], str2[100001];
    scanf("%s %s", str1, str2);  // 输入两个字符串
    printf("%d\n", checkInclusion(str1, str2));  // 输出结果
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

JS解法

  • 解题思路

  • 解题思路
    这道题目要求在字符串 str2 中查找是否有一个与字符串 str1 字母排列相同的子串,并返回第一个符合条件子串的起始位置。我们可以使用 滑动窗口 和 字符计数 技巧来高效地实现该功能。

思路概述:

字符计数:

通过两个字符计数数组,count1 用于统计 str1 中每个字符的频率,count2 用于统计 str2 中当前滑动窗口内每个字符的频率。
初始窗口:

初始化滑动窗口为 str2 中的前 len1 个字符(即 str1 的长度),并将其字符频率存储在 count2 数组中。
滑动窗口:

从 str2 的第一个字符开始,滑动窗口每次向右移动一个字符。每移动一步:
移除窗口最左边的字符(即 s2[i - str1.length]),并添加新的字符(即 s2[i])。
比较 count1 和 count2 是否相等,如果相等,说明当前窗口是 str1 的字母排列,返回当前窗口的起始位置。
结束条件:

如果找到了符合条件的子串,则返回其起始索引;如果遍历整个 str2 后都未找到,则返回 -1。

const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

// 监听输入行,获取用户输入
rl.on("line", (line) => {
  // 将输入的两个字符串按空格分割
  let [str1, str2] = line.split(" ");
  // 调用 findSubstring 函数并输出结果
  console.log(findSubstring(str1, str2));
});

// 主逻辑函数:查找 str2 中与 str1 字母排列相同的子串
function findSubstring(str1, str2) {
  // 初始化两个长度为 26 的数组用于记录字母的出现频率
  const count1 = Array(26).fill(0); // str1 中字符的频率
  const count2 = Array(26).fill(0); // 当前滑动窗口中字符的频率

  // 统计 str1 中每个字符的频率
  for (let i = 0; i < str1.length; i++) {
    count1[str1.charCodeAt(i) - 97]++;  // 'a' 的 ASCII 值是 97,所以使用 `charCodeAt(i) - 97` 得到字母对应的数组索引
  }

  // 统计 str2 中前 len1 个字符的频率
  for (let i = 0; i < str1.length; i++) {
    count2[str2.charCodeAt(i) - 97]++;
  }

  // 如果 str1 和 str2 的前 len1 个字符的频率相同,直接返回 0
  if (arraysEqual(count1, count2)) return 0;

  // 滑动窗口,从 str2 的第 len1 个字符开始滑动
  for (let i = str1.length; i < str2.length; i++) {
    // 移除窗口最左边的字符
    count2[str2.charCodeAt(i - str1.length) - 97]--;
    // 添加新字符到窗口
    count2[str2.charCodeAt(i) - 97]++;

    // 如果当前窗口的字符频率与 str1 相同,返回当前窗口的起始索引
    if (arraysEqual(count1, count2)) return i - str1.length + 1;
  }

  // 如果没有找到符合条件的子串,返回 -1
  return -1;
}

// 辅助函数:判断两个字符频率数组是否相等
function arraysEqual(arr1, arr2) {
  for (let i = 0; i < 26; i++) {
    if (arr1[i] !== arr2[i]) return false;  // 如果有任何位置的字符频率不同,返回 false
  }
  return true;  // 如果所有位置的字符频率相同,返回 true
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

注意:

如果发现代码有用例覆盖不到的情况,欢迎反馈!会在第一时间修正,更新。
解题不易,如对您有帮助,欢迎点赞/收藏

注:本文转载自blog.csdn.net的CodeClimb的文章"https://blog.csdn.net/CodeClimb/article/details/145099593"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top