学习内容:kmp算法
follow:
代码随想录讲解kmp算法
图解+讲解kmp算法
28 实现strStr
题目描述:
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
解析:
这道就是实现kmp算法
解答:
class Solution {
public static int strStr(String haystack, String needle) {
if(needle.length() == 0) return 0;
//kmp算法
//首先构造前缀表:每个数组下标表示,包含本字符的最长公共前后缀的长度
int[] next = getNext(needle);
//利用前缀表进行匹配
int j = 0;
for(int i = 0; i < haystack.length(); i++){
while(j > 0 && haystack.charAt(i) != needle.charAt(j)){
j = next[j - 1];
}
if(haystack.charAt(i) == needle.charAt(j)){
j++;
}
if(j == needle.length()){
return i - needle.length() + 1;
}
}
return -1;
}
public static int[] getNext(String s){
int[] next = new int[s.length()];
char[] needle = s.toCharArray();
//j代表了前缀末尾 + 1的位置,以及前缀的长度
int j = 0;
//初始化:next[0]的值为0,代表着第一个位置的公共前后缀的长度为0
next[0] = j;
//i代表了后缀末尾,以及遍历字符串的指针
for(int i = 1; i < needle.length; i++){
//如果目前正在匹配的后缀最后一位不相等
//则退回到next[j]
while(j > 0 && needle[i] != needle[j]){
j = next[j - 1];
}
//如果匹配,则当前j为前缀末尾,j移到下一位
if(needle[i] == needle[j]){
j++;
}
next[i] = j;
}
return next;
}
}
459. 重复的子字符串
题目描述:
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
解析:
使用kmp中的next数组
解答:
class Solution {
public boolean repeatedSubstringPattern(String s) {
//构造next数组
int[] next = getNext(s);
int len = s.length();
//len - (len - next[len - 1])可以被len整除,说明有重复的
if(next[len - 1] > 0 && len % (len - next[len - 1]) == 0){
return true;
}
return false;
}
public int[] getNext(String s){
int length = s.length();
int[] next = new int[length];
//初始化
int j = 0;
next[0] = j;
for(int i = 1; i < length; i++){
while(j > 0 && s.charAt(i) != s.charAt(j)){
j = next[j - 1];
}
if(s.charAt(i) == s.charAt(j)){
j++;
}
next[i] = j;
}
return next;
}
}
844 比较含退格的字符串
题目描述:
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
解析:
需要额外数组:从后向前遍历字符串,遇到#计数,遇到非#,根据计数进行回退
class Solution {
public boolean backspaceCompare(String s, String t) {
return backspace(s).equals(backspace(t));
}
public String backspace(String s){
StringBuilder stringBuilder = new StringBuilder();
int count = 0;
for(int i = s.length() - 1; i >= 0; i--){
if(s.charAt(i) == '#'){
count++;
}else{
if(count > 0){
count--;
}else{
stringBuilder.append(s.charAt(i));
}
}
}
return stringBuilder.toString();
}
}
76 最小覆盖字串
题目描述:
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
- 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
- 如果 s 中存在这样的子串,我们保证它是唯一的答案。
解析:
可以使用滑动窗口的方法
- 当滑动窗口内不包含字符串t时,right指针向右移动
- 当窗口内包含字符串t时,记录left、right,再向右移动left,直至不包含字符串t
自我认为的难点:如何判断窗口内包含字符串t——可使用一个计数count,当count==0时,滑动窗口包含所有字符串。
可以用数组保存字符串t出现的字符和次数,利用该数组决定count的值。
class Solution {
public String minWindow(String s, String t) {
if(s.length() == 0 || t.length() == 0 || s.length() < t.length()){
return "";
}
//用数组来保存,因为都是英文字母,所以是有限的,可以使用数组来存储
int[] record = new int[128];
for(char i : t.toCharArray()){
record[i]++;
}
//count用来衡量滑动窗口是否已经包含了t中所有字符
int count = t.length();
int left = 0, right = 0;
int result = Integer.MAX_VALUE;
int start = 0;
for(; right < s.length(); right++){
//说明目前滑动窗口没有包含所有t中字符
if(record[s.charAt(right)] > 0){
count--;
}
record[s.charAt(right)]--;
//滑动窗口中包含了t中所有字符:count == 0,record对应字符的索引<=0
while(left <= right && count == 0){
if((right - left) < result){
result = right - left + 1;
start = left;
}
record[s.charAt(left)]++;
//证明无法满足t中所有字符
if(record[s.charAt(left)] > 0){
count++;
}
left++;
}
}
if(result == Integer.MAX_VALUE){
return "";
}else{
return s.substring(start, start + result);
}
}
}