news 2026/4/3 4:54:24

(新卷,100分)- 关联子串(Java JS Python C)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
(新卷,100分)- 关联子串(Java JS Python C)

(新卷,100分)- 关联子串(Java & JS & Python & C)

题目描述

给定两个字符串str1和str2,如果字符串str1中的字符,经过排列组合后的字符串中,只要有一个字符串是str2的子串,则认为str1是str2的关联子串。

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

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

输入描述

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

输出描述

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

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

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

备注
  • 输入的字符串只包含小写字母;
  • 两个字符串的长度范围[1, 100000]之间;
用例
输入abc efghicbaiii
输出5
说明str2包含str1的一种排列组合("cab"),此组合在str2的字符串起始位置为5(从0开始计数)
输入abc efghiccaiii
输出-1
说明“abc”字符串中三个字母的各种组合(abc、acb、bac、bca、cab、cba),str2中均不包含,因此返回-1
题目解析

本题看上去是要求解全排列,但是题目描述数量级为:两个字符串的长度范围[1, 100000]之间;

因此str1的全排列肯定会超时。

本题可以使用尺取法来求解,尺取法其实就是高级一点的滑动窗口,常用于解决最小覆盖子串问题

大家可以认真看下上面这个博客,对尺取法有个大致了解。

尺取法,通常是有一个子串str1,和一个父串str2,我们需要在父串str2中找到包含str1子串所有字符的目标串target,不在意字符顺序。

解决方案很固定:

预处理动作:

  • 统计出子串str1中各字符的数量,记录到count中,这里的count容器通常选择128长度的数组,因为字符串中的字符通常都是ASCII码表的字符,而ASCII码只有128个,因此选择128长度的数组就可以涵盖到所有字符。
  • 定义一个total变量,来记录str1字符的总数(即str1的长度)

滑窗动作:

  • 初始滑窗,即父串str2的0~str1.length-1范围的滑窗。此时滑窗只有新增字符的过程,即滑窗左边界保持在0,而滑窗右边界从0运动到str1.length-1位置。
  • 后续滑窗,即父串str2的 i ~ i + str1.length - 1,其中 i >= 1,每次滑窗整体向右移动一格,即相较于上一个滑窗,会失去 str2[i-1]字符,以及新增 str2[i + str1.length - 1] 字符。

滑窗的意义:

  • 滑窗其实就是一个str2的子串,我们可以基于滑窗来统计滑窗内子串各字符的数量,如果滑窗内各字符的数量与str1各字符数量一致,则说明滑窗内子串就是一个目标子串

滑窗处理新增字符:

  • 当滑窗新增一个字符c时,我们应该count[c] -= 1,表示滑窗子串和str1的差别缩小了,此时total是否也需要-=1吗?total是str1的字符总数,此时需要细化讨论
  1. 如果字符c不是str1内的字符,则total不需要-=1
  2. 如果字符c是str1内的字符,此时又需要细化讨论,字符c虽然是str1内的字符,但是滑窗内c字符的数量完全有可能超过str1内的字符数量,即滑窗内存在多余的字符c,那么此时滑窗新增了一个c,并不会缩小和str1的差距,即total不需要-=1。

那么此时就需要搞清楚,如何判断滑窗内字符c是否是str1的字符?以及是str1字符的情况下,是否为超标字符?

上面两个判断,可以用一句话实现:count[c] > 0

  • 如果滑窗新增的字符c不是str1字符,则必然count[c] <= 0
  • 如果滑窗新增的字符c是str1字符,但是超标了,则经过count[c]--动作,超过的c字符,必然count[c] <= 0

滑窗处理移除字符:

当滑窗移除一个字符c是,我们应该count[c]++,表示滑窗子串和str1的差别扩大了,此时total是否也需要+=1吗?total是str1的字符总数,此时需要细化讨论:

  • 如果字符c不是str1内的字符,则total不需要+=1
  • 如果字符c是str1内的字符,此时又需要细化讨论,字符c虽然是str1内的字符,但是滑窗内c字符的数量完全有可能超过str1内的字符数量,即滑窗内存在多余的字符c,那么此时滑窗移除一个c,并不会扩大和str1的差距,即不需要total+=1

那么此时就需要搞清楚,如何判断滑窗内字符c是否是str1的字符?以及是str1字符的情况下,是否为超标字符?

上面两个判断,可以用一句话实现:count[c]>=0

  • 如果滑窗移除的字符c不是str1字符,则移除之前必然count[c] < 0
  • 如果滑窗移除的字符c是str1字符,但是超标了,则移除之前必然count[c] < 0
Java算法源码
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String str1 = sc.next(); String str2 = sc.next(); System.out.println(getResult(str1, str2)); } public static int getResult(String str1, String str2) { // count用于统计str1中各字符的数量 int[] count = new int[128]; for (int i = 0; i < str1.length(); i++) { char c = str1.charAt(i); count[c]++; } // total统计str1总共字符个数 int total = str1.length(); // 初始滑窗,滑窗范围内容,就是一个子串 for (int i = 0; i < str1.length(); i++) { // 滑窗子串新增的字符 char add = str2.charAt(i); // 如果新增字符是str1的字符,即count[add] > 0时,则说明滑窗子串已找到一个目标字符,此时剩余add字符数量count[add]--,剩余目标字符总数total-- if (count[add]-- > 0) { total--; } } // 如果total == 0,则说明滑窗内所有字符都是str1内的字符,由于限定了滑窗的长度就是str1的长度,因此滑窗内字符和str1完全匹配 if (total == 0) return 0; // 下一个滑窗范围是 i ~ i + str1.length() - 1 for (int i = 1; i + str1.length() - 1 < str2.length(); i++) { // 相较于上一个滑窗失去的字符remove char remove = str2.charAt(i - 1); // 相较于上一个滑窗新增的字符add char add = str2.charAt(i + str1.length() - 1); // 本轮滑窗remove字符,在上一轮是被add的字符,我们假设此字符为c,此时可以分两种情况讨论: // 1、c是str1的字符,则初始统计时count[c]>0,上一轮滑窗加入c字符,则count[c]--,此轮count[c]是有可能>=0或者<0的, // 1.1、如果本轮count[c]>=0,则说明上轮滑窗并没有找到所有的c字符,因此本轮移除的c字符可以还原total数量 // // 1.2、如果本轮count[c]<0,这说明上轮滑窗内的c字符超标了,即c字符是目标字符,但是滑窗内包含的c字符数量超过了str1中c字符的数量,因此本轮移除c字符是超标部分,不会还原total // 2、c不是str1的字符,则初始统计时count[c]==0,上一轮滑窗加入c字符,则count[c]--,此轮必然count[c]<0,因此c字符的移除,并不会还原total if (count[remove]++ >= 0) { total++; } if (count[add]-- > 0) { total--; } if (total == 0) { return i; } } return -1; } }
JS算法源码
/* JavaScript Node ACM模式 控制台输入获取 */ const readline = require("readline"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.on("line", (line) => { let [str1, str2] = line.split(" "); console.log(getResult(str1, str2)); }); function getResult(str1, str2) { // count用于统计str1中各字符的数量 const count = {}; for (let i = 0; i < str1.length; i++) { const c = str1[i]; count[c] = (count[c] ?? 0) + 1; } // total统计str1总共字符个数 let total = str1.length; // 初始滑窗,滑窗范围内容,就是一个子串 for (let i = 0; i < str1.length; i++) { // 滑窗子串新增的字符 const add = str2[i]; // 如果新增字符是str1的字符,即count[add] > 0时,则说明滑窗子串已找到一个目标字符,此时剩余add字符数量count[add]--,剩余目标字符总数total-- if (count[add]-- > 0) { total--; } } // 如果total == 0,则说明滑窗内所有字符都是str1内的字符,由于限定了滑窗的长度就是str1的长度,因此滑窗内字符和str1完全匹配 if (total == 0) return 0; // 下一个滑窗范围是 i ~ i + str1.length() - 1 for (let i = 1; i + str1.length - 1 < str2.length; i++) { // 相较于上一个滑窗失去的字符remove const remove = str2[i - 1]; // 相较于上一个滑窗新增的字符add const add = str2[i + str1.length - 1]; // 本轮滑窗remove字符,在上一轮是被add的字符,我们假设此字符为c,此时可以分两种情况讨论: // 1、c是str1的字符,则初始统计时count[c]>0,上一轮滑窗加入c字符,则count[c]--,此轮count[c]是有可能>=0或者<0的, // 1.1、如果本轮count[c]>=0,则说明上轮滑窗并没有找到所有的c字符,因此本轮移除的c字符可以还原total数量 // 1.2、如果本轮count[c]<0,这说明上轮滑窗内的c字符超标了,即c字符是目标字符,但是滑窗内包含的c字符数量超过了str1中c字符的数量,因此本轮移除c字符是超标部分,不会还原total // 2、c不是str1的字符,则初始统计时count[c]==0,上一轮滑窗加入c字符,则count[c]--,此轮必然count[c]<0,因此c字符的移除,并不会还原total if (count[remove]++ >= 0) { total++; } if (count[add]-- > 0) { total--; } if (total == 0) return i; } return -1; }
Python算法源码
# 输入获取 str1, str2 = input().split() # 算法入口 def getResult(): # count用于统计str1中各字符的数量 count = {} for c in str1: count[c] = count.get(c, 0) + 1 # total统计str1总共字符个数 total = len(str1) # 初始滑窗,滑窗范围内容,就是一个子串 for i in range(len(str1)): # 滑窗子串新增的字符 add = str2[i] # 如果新增字符是str1的字符,即count[add] > 0时,则说明滑窗子串已找到一个目标字符,此时剩余add字符数量count[add]--,剩余目标字符总数total-- if count.get(add) is not None: if count[add] > 0: total -= 1 count[add] -= 1 # 如果total == 0,则说明滑窗内所有字符都是str1内的字符,由于限定了滑窗的长度就是str1的长度,因此滑窗内字符和str1完全匹配 if total == 0: return 0 # 下一个滑窗范围是 i ~ i + str1.length() - 1 for i in range(1, len(str2) - len(str1) + 1): # 相较于上一个滑窗失去的字符remove remove = str2[i-1] # 相较于上一个滑窗新增的字符add add = str2[i + len(str1) - 1] # 本轮滑窗remove字符,在上一轮是被add的字符,我们假设此字符为c: # 1、c是str1的字符,则初始统计时count[c]>0,上一轮滑窗加入c字符,则count[c]--,此轮count[c]是有可能>=0或者<0的, # 1.1、如果本轮count[c]>=0,则说明上轮滑窗并没有找到所有的c字符,因此本轮移除的c字符可以还原total数量 # 1.2、如果本轮count[c]<0,这说明上轮滑窗内的c字符超标了,即c字符是目标字符,但是滑窗内包含的c字符数量超过了str1中c字符的数量,因此本轮移除c字符是超标部分,不会还原total if count.get(remove) is not None: if count[remove] >= 0: total += 1 count[remove] += 1 if count.get(add) is not None: if count[add] > 0: total -= 1 count[add] -= 1 if total == 0: return i return -1 # 算法调用 print(getResult())
C算法源码
#include <stdio.h> #include <string.h> #define MAX_LEN 100000 int getResult(char *str1, char *str2); int main() { char str1[MAX_LEN], str2[MAX_LEN]; scanf("%s %s", str1, str2); printf("%d\n", getResult(str1, str2)); return 0; } int getResult(char *str1, char *str2) { // count用于统计str1中各字符的数量 int count[128] = {0}; for (int i = 0; i < strlen(str1); i++) { char c = str1[i]; count[c]++; } // total统计str1总共字符个数 int total = strlen(str1); // 初始滑窗,滑窗范围内容,就是一个子串 for (int i = 0; i < strlen(str1); i++) { // 滑窗子串新增的字符 char add = str2[i]; // 如果新增字符是str1的字符,即count[add] > 0时,则说明滑窗子串已找到一个目标字符,此时剩余add字符数量count[add]--,剩余目标字符总数total-- if (count[add]-- > 0) { total--; } } // 如果total == 0,则说明滑窗内所有字符都是str1内的字符,由于限定了滑窗的长度就是str1的长度,因此滑窗内字符和str1完全匹配 if (total == 0) return 0; // 下一个滑窗范围是 i ~ i + str1.length() - 1 for (int i = 1; i + strlen(str1) - 1 < strlen(str2); i++) { // 相较于上一个滑窗失去的字符remove char remove = str2[i - 1]; // 相较于上一个滑窗新增的字符add char add = str2[i + strlen(str1) - 1]; // 本轮滑窗remove字符,在上一轮是被add的字符,我们假设此字符为c,此时可以分两种情况讨论: // 1、c是str1的字符,则初始统计时count[c]>0,上一轮滑窗加入c字符,则count[c]--,此轮count[c]是有可能>=0或者<0的, // 1.1、如果本轮count[c]>=0,则说明上轮滑窗并没有找到所有的c字符,因此本轮移除的c字符可以还原total数量 // // 1.2、如果本轮count[c]<0,这说明上轮滑窗内的c字符超标了,即c字符是目标字符,但是滑窗内包含的c字符数量超过了str1中c字符的数量,因此本轮移除c字符是超标部分,不会还原total // 2、c不是str1的字符,则初始统计时count[c]==0,上一轮滑窗加入c字符,则count[c]--,此轮必然count[c]<0,因此c字符的移除,并不会还原total if (count[remove]++ >= 0) { total++; } if (count[add]-- > 0) { total--; } if (total == 0) { return i; } } return -1; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/2 3:32:46

师大校友惠超市管理系统微信小程序-计算机毕业设计源码+LW文档

摘 要 随着时代的迅猛发展&#xff0c;各行各业都在积极采纳先进技术以提升自身实力和竞争优势&#xff0c;师大校友惠超市管理系统自然也不例外。这款师大校友惠超市管理的开发&#xff0c;是基于实际应用需求与软件工程原理&#xff0c;运用了微信开发者工具、Java编程语言以…

作者头像 李华
网站建设 2026/3/8 22:56:44

一文搞懂:AI上下文理解中的实体链接技术

一文搞懂:AI上下文理解中的实体链接技术 引言:从日常对话到AI理解的鸿沟 "帮我预订明天去北京的机票,顺便查查三里屯附近有什么好吃的日料店。“这句看似简单的人类对话,对AI系统而言却蕴含着巨大的理解挑战。其中"北京”、“三里屯”、"日料店"这些…

作者头像 李华
网站建设 2026/4/1 4:15:01

【复现】含能量路由器的交直流混合配电网潮流计算Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f447; 关注我领取海量matlab电子书和数学建模资料 &#x1…

作者头像 李华
网站建设 2026/3/23 12:01:29

大数据领域数据架构的智能餐饮应用

大数据领域数据架构的智能餐饮应用&#xff1a;从“厨房账本”到“智慧餐厅”的进化之旅关键词&#xff1a;大数据架构、智能餐饮、数据采集、实时分析、业务决策 摘要&#xff1a;本文以智能餐饮场景为切入点&#xff0c;结合大数据数据架构的核心环节&#xff08;采集-存储-处…

作者头像 李华
网站建设 2026/4/1 10:19:33

学霸同款10个AI论文网站,MBA毕业论文轻松搞定!

学霸同款10个AI论文网站&#xff0c;MBA毕业论文轻松搞定&#xff01; AI 工具助力论文写作&#xff0c;高效又省心 在当今这个信息爆炸的时代&#xff0c;MBA 学习者和研究者面对的不仅是繁重的课程压力&#xff0c;还有日益复杂的论文写作任务。如何在有限的时间内完成高质量…

作者头像 李华
网站建设 2026/4/1 23:02:43

智界·无界:高德开发者生态大会深度观察与技术巡礼

在地理信息系统&#xff08;GIS&#xff09;与移动互联网深度融合的当下&#xff0c;高德地图作为行业内的基础设施提供者&#xff0c;其开发者生态的动向往往预示着位置服务&#xff08;LBS&#xff09;技术的下一个风口。本次参与高德开发者大会&#xff0c;不仅仅是一次简单…

作者头像 李华