当前位置: 首页 > news >正文

字符串 下【KMP再续】

⚡刷题计划day9继续,可以点个免费的赞哦~

往期可看专栏,关注不迷路,

您的支持是我的最大动力🌹~

今天的重头戏是KMP算法的理解,如果是第一次接触可能会有难度,可以结合以下链接的讲解及对应视频,没有明白可以多看多听几遍,加油!然后下一期就是双指针专题了,欢迎点赞收藏关注!

(programmercarl.com)

题目一:28. 找出字符串中第一个匹配项的下标

leetcode:28. 找出字符串中第一个匹配项的下标

(https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/)

法一:一般思路

法一就是暴力解,直接遍历两个串,比较是否匹配。

详见注释

class Solution {public int strStr(String haystack, String needle) {// 获取haystack和needle的长度int n1 = haystack.length();int n2 = needle.length();
​// 如果haystack的长度小于needle的长度,直接返回-1,表示找不到if(n1 < n2){return -1;}
​// 将haystack和needle转换为字符数组char[] c1 = haystack.toCharArray();char[] c2 = needle.toCharArray();
​// 遍历haystackfor(int i = 0; i < n1; i++){// 初始化搜索的起始和结束索引int start = i;int end = i + n2 - 1;// 如果结束索引超出haystack的范围,跳出循环if(end >= n1){break;}
​// 初始化一个标志位,用于判断是否找到匹配的子串boolean flag = true;int k = 0;// 内层循环,用于比较字符while(start <= end){// 如果当前字符不匹配,设置标志位为false并跳出循环if(c1[start++] != c2[k++]){flag = false;break;}}// 如果标志位为true,表示找到了匹配的子串,返回当前索引iif(flag){return i;}}
​// 如果遍历完haystack都没有找到needle,返回-1return -1;}
}

法二:KMP算法

详见注释

class Solution {// getNext 方法用于构建 next 数组,该数组用于存储模式字符串的最长前后缀匹配的长度public void getNext(int[] next, String s) {int j = -1; // j 用于记录当前比较的子字符串的最后一个匹配位置next[0] = j; // 模式字符串的第一个字符没有前缀,所以它的 next 值是 -1for (int i = 1; i < s.length(); i++) { // 从模式字符串的第二个字符开始遍历while (j >= 0 && s.charAt(i) != s.charAt(j + 1)) { // 当前字符不匹配时,尝试回溯j = next[j]; // 根据 next 数组回溯到上一个匹配的位置}if (s.charAt(i) == s.charAt(j + 1)) { // 如果当前字符匹配,更新 jj++;}next[i] = j; // 更新 next 数组,存储当前位置的最长前后缀匹配的长度}}
​// strStr 方法用于在 haystack 字符串中查找 needle 字符串出现的位置public int strStr(String haystack, String needle) {if (haystack.length() < needle.length()) { // 如果文本字符串比模式字符串短,直接返回 -1return -1;}
​int[] next = new int[needle.length()]; // 创建 next 数组getNext(next, needle); // 构建 next 数组
​int j = -1; // 初始化 j 为 -1,表示在模式字符串中的位置for (int i = 0; i < haystack.length(); i++) { // 遍历文本字符串while (j >= 0 && haystack.charAt(i) != needle.charAt(j + 1)) { // 如果当前字符不匹配,根据 next 数组回溯j = next[j]; // 回溯到上一个匹配的位置}if (haystack.charAt(i) == needle.charAt(j + 1)) { // 如果当前字符匹配,更新 jj++;}if (j == needle.length() - 1) { // 如果 j 到达模式字符串的末尾,表示找到了匹配return (i - needle.length() + 1); // 返回模式字符串在文本字符串中的起始位置}}
​return -1; // 如果遍历完文本字符串都没有找到匹配,返回 -1}
}

题目二:459.重复的子字符串

459.重复的子字符串

(https://leetcode.cn/problems/repeated-substring-pattern/description/)

法一:暴力解法

解题思路:

如果字符串 S 包含一个重复的子字符串,则可以通过多次移位原字符串,最终能得到一新相同字符串与原字符串匹配。

例:abcabc

移位一次:cabcab 移位两次:bcabca 移位三次:abcabc

现在字符串和原字符串匹配了,所以可以得出结论存在重复的子串。

但是对于重复字符串比较长的情况,效率就会很低,并且超时。

基于此思想,可以新建一个字符串str = s + s,等于原字符串s再加上自身,这样新的字符串str就包含了所有移动的情况。

比如字符串:s = acd,那么 str = s + s = acdacd

acd 移动的可能:dac、cda。其实都包含在了 str 中了。就像一个滑动窗口

一开始 acd (acd) ,移动一次 ac(dac)d,移动两次 a(cda)cd。循环结束.

注意需要去除 str 中首尾元素

 所以可以直接判断之后,是否包含自身元素。如果包含。则表明存在重复子串。

附一网友理解:如果S不包含重复的子字符串,则S本身就是所谓的“重复子字符串”(这里方便自己理解,视为重复一次,不深究= =),str = S+S,说明S是str的重复子字符串,刨去str首尾两个字符之后(相当于分别破坏前一个S头部和后一个S尾部),不能满足str包含S。

AC代码

class Solution {public boolean repeatedSubstringPattern(String s) {String t = s + s;t = t.substring(1, t.length() - 1); 	// 掐头去尾if (t.contains(s)) {return true;}return false;}
}

法二:KMP

关于kmp还不是很理解的可以再康康上期附送的链接,这里不再多叙述

然后此题需要证明,有兴趣的小伙伴也可以康康这个链接:

(https://programmercarl.com/0459.重复的子字符串.html#思路)

我们这里直接给出判断的结论:

如果len % (len - (next[len - 1] + 1)) == 0 ,即数组的长度正好可以被 最长相等前后缀不包含的子串的长度 整除 ,说明该字符串有重复的子字符串。

class Solution {public void getNext(int[] next,String s){int j=-1;next[0]=j;for(int i=1;i<s.length();i++){while (j>=0 && s.charAt(i)!=s.charAt(j+1)){j = next[j];}if(s.charAt(i)==s.charAt(j+1)){j++;}next[i] = j;}}public boolean repeatedSubstringPattern(String s) {if(s.equals("")) return false;int len = s.length();int j=-1;int[] next = new int[len];getNext(next,s);////1.如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀//2.最长相等前后缀的长度为:next[len - 1] + 1。(这里的next数组是以统一减一的方式计算的,因此需要+1//3.len - (next[len - 1] + 1) 是最长相等前后缀不包含的子串的长度。if(next[len-1] != -1 && len % (len-(next[len-1]+1))==0){return true;}return false;}
}

下期开启新专题,感谢点赞收藏关注哦🌹


http://www.mrgr.cn/news/33275.html

相关文章:

  • GitHub每日最火火火项目(9.22)
  • 【Elasticsearch系列十八】Ik 分词器
  • 解锁电商新视野:京东商品详情API——您的精准商品信息探索利器
  • Java后端中的延迟队列实现:使用Redis与RabbitMQ的不同策略
  • AI学习指南深度学习篇-Adadelta简介
  • JavaScript(二)
  • 【Linux 从基础到进阶】 AWS云服务在Linux上的应用
  • C\C++内存管理详解
  • PHP在将数据存储到数据库之前如何转义数据
  • Java项目实战II基于Java+Spring Boot+MySQL的植物健康系统(开发文档+源码+数据库)
  • 算法题之每日温度
  • python发送邮件 - email smtplib
  • SOMEIP_ETS_122: SD_Interface_Version
  • Linux文件IO(七)-复制文件描述符
  • Codeforces Round 974 (Div. 3) B. Robin Hood and the Major Oak
  • 通信工程学习:什么是NFV网络功能虚拟化
  • C++primer第十一章使用类(矢量随机游走实例)
  • 详细分析Spring的动态代理机制
  • LeetCode题练习与总结:回文链表--234
  • 栈和队列(选择题)