leetcode字符串刷题总结

文章详细介绍了KMP算法,包括其理论基础、next数组的计算以及在解决字符串匹配问题中的应用。此外,还讨论了字符串的反转旋转系列问题,如分段反转、替换空格和左旋转字符串。接着,文章涵盖了括号问题,如有效括号字符串的验证和处理。最后,提到了字符串解码和特定公司的面试题,展示了如何利用栈解决这类问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、KMP算法 ****

1. 理论

  • 主要思想
    当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。
    next数组
  • next数组和前缀表
    针对模式串而言
    next数组既可以就是前缀表,也可以是前缀表统一减一(右移一位,初始位置为-1)。
  • 核心概念
    前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串
    模式串与前缀表对应位置的数字表示的就是: 下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
    在这里插入图片描述
  • 做题思路 两部曲
    对模式串求next数组(难点):双指针,一个指向后缀的最后一个元素,一个指向前缀的最后一个元素
    利用数组来优化,再文本串中找模式串
  • 如何利用 前缀表找到 当字符不匹配的时候应该指针应该移动的位置
    找到的不匹配的位置, 那么此时我们要看它的前一个字符的前缀表的数值是多少。
    宫水三叶解释

T459. 重复的子字符串(用next数组的特点求解)

在这里插入图片描述
假设字符串s使用多个重复子串构成(这个子串是最小重复单位),重复出现的子字符串长度是x,所以s是由n * x组成。

因为字符串s的最长相同前后缀的长度一定是不包含s本身,所以 最长相同前后缀长度必然是m * x,而且 n - m = 1,
如果len % (len - (next[len - 1] + 1)) == 0 ,则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。

数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。

class Solution {
    //kmp算法:利用next数组
    public boolean repeatedSubstringPattern(String s) {
        int len = s.length();
        int[] next = new int[len];
        next[0] = 0;
        int j = 0;//指向前缀的最后一个元素
        for(int i=1;i<len;i++){//i指向后缀的最后一个元素
            while(j>0&&s.charAt(i) != s.charAt(j)){//不同,j指针不断往回
                j = next[j-1];
            }
            if(s.charAt(i) == s.charAt(j)){//相同
                j++;
            }
            next[i] = j;
        }
        if(len%(len-next[len-1])==0 && next[len-1]>0){
            return true;
        }
        return false;
    }
}

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

在这里插入图片描述
时间复杂度:o(m+n)

class Solution {
    //kmp算法
    //称haystack为文本串;needle为模式串
    public int strStr(String haystack, String needle) {
        int[] next = getNext(needle);//由模式串获得next数组
        //遍历文本串,只需要遍历1次,i递增
        //遍历模式串,j不是递增的
        int j = 0;
        for(int i = 0;i<haystack.length();i++){
            while(j>0 && needle.charAt(j) != haystack.charAt(i)){//文本串和模式串不匹配
                j = next[j-1];//j不需要从头遍历 可以根据next跳转
            }
            if(haystack.charAt(i) == needle.charAt(j)){
                j++;
            }
            if(j == needle.length()){
                return i-needle.length()+1;
            }
        }
        return -1;
    }
    //找出模式串的next数组
    //next表示记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀
    public int[] getNext(String needle){
        //初始化
        int j = 0;
        int[] next = new int[needle.length()];
        next[0] = 0;
        //从1开始遍历
        for(int i = 1;i<needle.length();i++){
            while(j>0 && needle.charAt(i) != needle.charAt(j)){
                j = next[j-1];//字符不相等. 难点1
            }
            if(needle.charAt(i) == needle.charAt(j)){
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}

二. 反转旋转系列

T541. 反转字符串Ⅱ (分段反转字符,字符数组)

在这里插入图片描述

class Solution {
    public String reverseStr(String s, int k) {
        char[] sc = s.toCharArray();
        for(int i=0;i<s.length();i+=2*k){
            int start = i;
            int end = Math.min(start+k-1,s.length());
            while(start<end){
                char temp = sc[end];
                sc[end] = sc[start];
                sc[start] = temp;
                start++;
                end--;
            }
        }
        return new String(sc);
    }
}

剑指Offer 05.替换空格 (StringBuilder)

在这里插入图片描述

class Solution {
    public String replaceSpace(String s) {
        if(s.length()==0) return "";
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)==' '){
                sb.append("%20");
            }else{
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }
}

T151. 反转字符串中的单词(翻转字符+移除空格) ***

在这里插入图片描述

class Solution {
    public String reverseWords(String s) {
        //1.删除空格
        //2.翻转整个字符串
        //3.翻转单词
        String sb  = removeSpace(s);
        int start = 0;
        int end = sb.length() - 1;
        String reverseStr = reverseStr(sb,start,end);
        String str = reverseWord(reverseStr);
        return str;
    }

    //1.移除空格
    public String removeSpace(String s){
        StringBuilder sb = new StringBuilder();
        int start = 0;
        int end = s.length()-1;
        //移除开头和结尾的空格
        while(s.charAt(start) == ' ') {start++;}
        while(s.charAt(end) == ' '){ end--;}
        while(start<=end){
            char c = s.charAt(start);
            //c不为空格
            //c为空格但是sb字符串的最后一个字符不是空格
            if(c != ' ' || sb.charAt(sb.length()-1) != ' '){
                sb.append(c);
            }
            start++;
        }
        return sb.toString();
       }
    //2.翻转字符串
    public String reverseStr(String s, int start, int end){
        char[] schar = s.toCharArray();
        while (start<end){
            char temp = schar[start];
            schar[start] = schar[end];
            schar[end] = temp;
            start++;
            end--;
        }
        return new String(schar);
    }
    public String reverseWord(String s){
        //从前开始遍历,遇到空格和到达结尾就翻转字符串s
        int start = 0;
        int end = 1;
        int len = s.length();
        String sb = s;
        while(start<len){
            while(end<len && s.charAt(end) != ' '){
                end++;
            }
            sb = reverseStr(sb,start,end-1);
            start = end+1;
            end = start+1;
        }
        return sb;
    }
}

剑指 Offer 58 - II. 左旋转字符串(附加要求:不能申请额外空间,只能在本串上操作)

在这里插入图片描述

class Solution {
    //不用额外空间
    public String reverseLeftWords(String s, int n) {
        char[] sc = s.toCharArray();
        //翻转前n个
        reverseHelp(sc,0,n-1);
        //翻转n到 len个
        reverseHelp(sc,n,sc.length-1);
        //整体翻转
        reverseHelp(sc,0,sc.length-1);
        return new String(sc);
    }
    public void reverseHelp(char[] s, int start, int end){
        while(start<end){
            char temp = s[start];
            s[start] = s[end];
            s[end] = temp;
            start++;
            end--;
        }
    }
}
class Solution {
    public String reverseLeftWords(String s, int n) {
        return s.substring(n) + s.substring(0,n);
    }
}

三. 括号问题

T678. 有效的括号字符串(栈模拟 / dp)

在这里插入图片描述
两个栈模拟
两个栈存放索引,方便后续左括号和*号进行比较

class Solution {
    public boolean checkValidString(String s) {
        Deque<Integer> leftStack = new LinkedList<>();//放的是索引,方便后续判断
        Deque<Integer> star = new LinkedList<>();
        //遇到左括号和*号入栈,遇到右括号先判断左括号,再判断*号
        for(int i=0;i<s.length();i++){
            char c = s.charAt(i);
            if(c == '('){
                leftStack.push(i);
            }else if(c == '*'){
                star.push(i);
            }else if(c == ')'){
                if(!leftStack.isEmpty()){
                    leftStack.pop();
                }else if(!star.isEmpty()){
                    star.pop();
                }else {
                    return false;
                }
            }
        }
        //处理左括号和*
        //要左和*匹配,则左的索引必须小于*的索引
        while(!leftStack.isEmpty() && !star.isEmpty()){
            int leftIndex = leftStack.pop();
            int starIndex = star.pop();
            if(leftIndex>starIndex){
                return false;
            }
        }
        return leftStack.isEmpty();
    }
}

T20. 有效的括号

T22. 括号生成

T32. 最长有效括号

T301. 删除无效的括号

0. 面试真题

T394.字符串解码(栈+字符串) *** 华为

在这里插入图片描述

class Solution {
    //辅助栈+模拟
    public String decodeString(String s) {
        StringBuilder res = new StringBuilder();
        int num = 0;//用来记录遍历到的数字,因为要乘10处理
        Deque<Integer> stackInt = new LinkedList<>();
        Deque<String> stackStr = new LinkedList<>();//辅助栈:保存处理好的str

        for(Character c:s.toCharArray()){
            if(c == '['){//数字和字母都入栈
                stackStr.push(res.toString());//把之前处理好的字符入栈
                stackInt.push(num);
                num = 0;//清空数字
                res = new StringBuilder();//清空字符
            }else if(c == ']'){
                int multi = stackInt.pop();//取出要乘的倍数
                StringBuilder temp = new StringBuilder();
                for(int i =0;i<multi;i++){
                    temp.append(res);
                }
                res = new StringBuilder(stackStr.pop()+temp);
            }else if(c >='0' && c<='9'){
                num = num*10 + Integer.parseInt(String.valueOf(c));
            }else{
                res.append(c);
            }
        }
        return res.toString();

    }
}

压缩算法:腾讯,与上一题类似 ***

在这里插入图片描述

import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;

public class Main{
    public static  void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        StringBuilder res = new StringBuilder();
        int num = 0;
        Deque<Integer> stackNum = new LinkedList<>();
        Deque<String> stackStr = new LinkedList<>();
        for (char c : line.toCharArray()) {
            if(c == '['){
                stackStr.push(res.toString());
                res = new StringBuilder();
            }else if(c == '|'){
                stackNum.push(num);
                num=0;
            }else if(c >= '0' && c <= '9'){
                num = num*10 + c - '0';
            }else if(c == ']'){
                int multi = stackNum.pop();
                StringBuilder sb = new StringBuilder();
                for(int i=0;i<multi;i++){
                    sb.append(res);
                }
                res = new StringBuilder(stackStr.pop() + sb);
            }else{
                res.append(c);
            }
        }
        System.out.println(res.toString());

    }
}

华为 HJ27 查找兄弟单词

在这里插入图片描述

import java.util.Scanner;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        String[] wordSet = new String[n];
        for (int i = 0; i < n; i++) {
            wordSet[i] = in.next();
        }
        String target = in.next();
        int m = in.nextInt();
        int len = target.length();
        int count = 0;
        List<String> res = new ArrayList<>();
        char[] tempTarget = target.toCharArray();
        Arrays.sort(tempTarget);
        for (int i = 0; i < n; i++) {
            if (wordSet[i].length() != len || target.compareTo(wordSet[i]) == 0) {
                continue;
            }
            char[] temp = wordSet[i].toCharArray();
            Arrays.sort(temp);
            Arrays.sort(tempTarget);
            String str = String.valueOf(temp);
            if (str.equals(String.valueOf(tempTarget))) {
                count++;
                res.add(wordSet[i]);
            }
        }
        System.out.println(count);
        if(m<res.size()){
            Collections.sort(res);
            System.out.println(res.get(m-1));
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值