代码随想录算法训练营第九天|151.翻转字符串里的单词 卡码网:55.右旋转字符串 28. 实现 strStr() 459.重复的子字符串 字符串总结 双指针回顾

今天也是要刷题的一天 学算法学八股的一天

151.翻转字符串里的单词

题目链接:151. 反转字符串中的单词 - 力扣(LeetCode)

讲解链接:字符串复杂操作拿捏了! | LeetCode:151.翻转字符串里的单词

 思路很清晰 1 先去掉所有的空格(首尾)

2 把所有的字符全部翻转

3 把局部的单词局部翻转

完成

Java代码:

class Solution {
    /*
    不使用java内置函数
    ** 1 去除首尾以及中间多余空格
       2 反转整个字符串
       3 翻转每个单词
     */
    /*
    示例:
    输入:"  hello world  "
    去除多余空格后:"hello world"
    反转整个字符串:"dlrow olleh"
    反转每个单词:"world hello"
     */
    public String reverseWords(String s) {
        //1 去除首尾以及中间多余空格
        StringBuilder sb = removeSpace(s);
        //2 翻转整个字符串
        reverseString(sb, 0,sb.length() - 1);
        //3 翻转每个单词
        reverseEachWord(sb);
        return sb.toString();
    }

    private StringBuilder removeSpace(String s){
        int start = 0;
        int end = s.length() - 1;
        while(s.charAt(start) == ' ') start++;
        while(s.charAt(end) == ' ') end--;
        StringBuilder sb = new StringBuilder();
        while(start <= end){
            char c = s.charAt(start);
            if(c != ' ' || sb.charAt(sb.length() - 1) != ' '){
                sb.append(c);
            }
            start++;
        }
        return sb;
    }
    /*
    *反转字符串指定区间start end 的区间
     */
    public void reverseString(StringBuilder sb, int start, int end){
        while(start < end){
            char temp = sb.charAt(start);
            sb.setCharAt(start, sb.charAt(end));
            sb.setCharAt(end, temp);
            start++;
            end--;
        }
    }

    private void reverseEachWord(StringBuilder sb){
        int start = 0;
        int end = 1;
        int n = sb.length();
        while(start < n){
            while(end < n && sb.charAt(end) != ' '){
                end++;
            }
            reverseString(sb, start, end - 1);
            start = end + 1;//起始点改变为上一个end的结尾
            end = start + 1;//终止点变为当前start的下一位
        }
    }
}

 学习思路 多敲代码!!!!!!!

卡码网:55.右旋转字符串

题目链接:55. 右旋字符串(第八期模拟笔试)

讲解链接:右旋字符串 | 代码随想录

 也是字符串反转 多做做就熟悉了 

思路 1 反转整个字符串

2 翻转0到字符串length - n - 1

3翻转n到字符串length() -  1 返回字符串 得到结果

Java代码:

import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = Integer.parseInt(in.nextLine());
        String s = in.nextLine();

        int len = s.length();
        char[] chars = s.toCharArray();
        reverseString(chars, 0, len - 1);//反转整个字符串
        reverseString(chars, 0, n - 1);//反转前一段字符串 此时字符串首尾 是0 len - n - 1
        reverseString(chars, n, len - 1);//反转后一段字符串 此时字符串首尾 是n len - 1

        System.out.println(chars);
    }
    public static void reverseString(char[] ch, int start, int end){
        //异或法反转字符串 参照题目344反转字符串的解释
        while(start < end){
            //异或做交换必须要会
            ch[start] ^= ch[end];
            ch[end] ^= ch[start];
            ch[start] ^= ch[end];
            start++;
            end--;
        }
    }
}

需要对思路清晰 和对字符串的创建和处理 多写写 还有swap环节用异或运算完成交换

28. 实现 strStr() 

题目链接:代码随想录

讲解链接:代码随想录

三叶给的解答:28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)

在这里感谢三叶大佬 向大佬学习 kmp在他解释下我感觉清晰不少

KMP算法 KMP算法 KMP算法 第一次接触 懵懵懂懂 看完代码随想录 写!

Java代码(KMP 完全不会,):

class Solution {
    //KMP算法
    //原串:origin 匹配串:needle
    public int strStr(String origin, String needle) {
        if(needle.isEmpty()) return 0;

        //分别读取原串和匹配串的长度
        int n = origin.length(), m = needle.length();
        //原串和匹配串前面都加空格 使其下标都从 1 开始
        origin = " " + origin;
        needle = " " + needle;

        char[] o = origin.toCharArray();
        char[] pp = needle.toCharArray();

        //构造 next 数组 数组长度为匹配串的长度(next 数组是和匹配串相关的
        int[] next = new int[m + 1];
        //构造过程 i = 2 j = 0 开始,i 小于等于匹配串长度[构造 i 从 2 开始]
        for(int i = 2, j = 0; i <= m; i++){
            //匹配不成功 j = next[j]
            while(j > 0 && o[i] != o[j + 1]) j = next[j];
            //匹配成功 先让j++
            if(pp[i] == pp[j + 1]) j++;
            //更新next[i] 结束本次循环 i++
            next[i] = j;
        }

        //匹配过程 i = 1 j = 0 开始 i 小于等于原串长度【匹配从 i 到 1 开始】
        for(int i = 1, j = 0; i <= n; i++){
            //匹配不成功 j = next[j]
            while(j > 0 && o[i] != pp[j + 1]) j = next[j];
            //匹配成功 先让j++ 结束本次循环 i++
            if(o[i] == pp[j + 1]) j++;
            //整一段匹配成功 直接返回下标
            if(j == m) return i - m;
        }
        return -1;
    }
}

Java代码(滑动窗口):

class Solution {
    public int strStr(String haystack, String needle) {
        示例:
        假设haystack = "hello world",needle = "world":
        外层循环开始,i = 0。
        内层循环找到haystack中的第一个w(i = 6)。
        继续比较,j从0开始,i和j同步增加,直到j == m。
        needle的所有字符都匹配成功,返回needle的起始索引6。
        如果haystack = "hello world",needle = "xyz":

        外层循环开始,i = 0。
        内层循环无法找到匹配的字符,i不断增加,直到i == n。
        没有找到needle,返回-1。

        //滑动窗口
        int m = needle.length();
        //当needle是空字符串时应该返回0
        if(m == 0) return 0;
        int n = haystack.length();
        if(n < m) return -1;
        int i = 0,j = 0;
        while(i < n - m + 1){
            //找到首字母相等
            while(i < n && haystack.charAt(i) != needle.charAt(i)){
                i++;
            }
            if(i == n) return -1;//没有首字母相等的
            //遍历后续字符 判断是否相等
            i++;
            j++;
            while(i < n && j < m && haystack.charAt(i) == needle.charAt(j)){
                i++;
                j++;
            }if(j == m){
                //如果needle的所有字符都匹配成功,
                //返回needle在haystack中的起始索引。
                return i - j;
            }else{
                //未找到
                //如果没有找到完整的needle,
                //重置j为0,并将i回退到上一个位置
                //以便尝试下一个可能的匹配。
                i -= j - 1;
                j = 0;
            }
        }
        return -1;
    }
}

 459.重复的子字符串

题目链接:459. 重复的子字符串 - 力扣(LeetCode)

讲解链接:字符串这么玩,可有点难度! | LeetCode:459.重复的子字符串

暴力会超时 KMP会解决一切,

Java代码(暴力):

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        for(int i = 1; i < s.length(); i++){
            String str = rotate(s.toCharArray(), i);
            if(s.equals(str)) return true;
        }
        return false;
    }

    示例
    假设我们有一个字符串s = "abab":
    对于i = 1,旋转字符串得到"baba",与原字符串不相等。
    对于i = 2,旋转字符串得到"abab",与原字符串相等。
    因此,repeatedSubstringPattern("abab")将返回true,因为"abab"是其自身的重复子串模式。

    这个方法用于将一个字符数组nums向右旋转k个位置。
    首先,计算k对数组长度的模,以确保k不会超过数组的实际长度。
    然后,通过调用三次reverse方法来实现旋转:
    将整个数组反转。
    将数组的前k个元素反转。
    将数组的剩余元素(从索引k到末尾)反转。
    最后,将旋转后的字符数组转换为字符串并返回。

    public String rotate(char[] nums, int k){
        k = k % nums.length;
        reverse(nums, 0, nums.length - 1);//i = 1 baba i = 2  baba
        reverse(nums, 0 , k - 1);//i = 1 b不变 baba i = 2 abba
        reverse(nums, k , nums.length - 1);//aba反转也不变 所以还是baba i = 2 abab
        return String.valueOf(nums);
    }

    reverse 方法
    这个方法用于反转字符数组nums中从索引begin到end的子数组。
    使用两个指针i和j,分别指向子数组的开始和结束位置。
    在i小于j的情况下,交换i和j位置的元素,然后i向后移动一位,j向前移动一位。
    重复上述过程,直到i和j相遇或交叉。

    public void reverse(char[] nums, int start, int end){
        while(start < end){
            char temp = nums[start];
            nums[start] = nums[end];
            nums[end] = nums[start];
            start++;
            end--;
        }
    }
}

Java代码(KMP):

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        //KMP
        if(s.equals("")) return false;
        int len = s.length();
        //原始串加个空格 (哨兵)使得下标从1开始 这样j从0开始 也不用初始化
        s = " " + s;
        char[] chars = s.toCharArray();
        int[] next = new int[len + 1];

        //构造next数组过程 j从0开始(空格) i 从 2 开始
        for(int i = 2,j = 0; i <= len; i++){
            //匹配成功 j 回到前一位置 next 数组所对应的值
            while(j > 0 && chars[i] != chars[j + 1]) j = next[j];
            //匹配成功 j往后移
            if(chars[i] == chars[j + 1]) j++;
            //更新 next 数组的值
            next[i] = j;
        }

        //最后判断是否是重复子字符串 这里next[len]即代表next数组末尾的值
        if(next[len] > 0 && len % (len - next[len]) == 0){
            return true;
        }
        return false;
    }
}

 哇 KMP真的看不懂 还得再写写双指针 去看看左神的课 ! 今天也需要努力刷题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值