LeetCode刷题:字符串

字符串转数组

用数组的各种技巧(双指针、滑动窗口等解题)

例题1:基本双指针类操作

题目链接

在这里插入图片描述

class Solution {
    
    
    public String reverseStr(String s, int k) {
    
    
        char[] a = s.toCharArray();
        for (int start = 0; start < a.length; start += 2 * k) {
    
    
            int i = start, j = Math.min(start + k - 1, a.length - 1);
            while (i < j) {
    
    
                char tmp = a[i];
                a[i++] = a[j];
                a[j--] = tmp;
            } // 也可以直接reverse
        }
        return new String(a);
    }
}

在这里插入图片描述
// split形成新的字符串数组,然后反转即可

class Solution {
    
    
    public String reverseWords(String s) {
    
    
        // 除去开头和末尾的空白字符
        s = s.trim();
        // 正则匹配连续的空白字符作为分隔符分割
        List<String> wordList = Arrays.asList(s.split("\\s+"));
        Collections.reverse(wordList);
        return String.join(" ", wordList);
    }
}

在这里插入图片描述

class Solution {
    
    
    public String replaceSpace(String s) {
    
    
        int length = s.length();
        char[] array = new char[length * 3]; // 最坏情况是三倍
        int size = 0; // 指针1
        for (int i = 0; i < length; i++) {
    
     // 指针2
            char c = s.charAt(i);
            if (c == ' ') {
    
    
                array[size++] = '%';
                array[size++] = '2';
                array[size++] = '0';
            } else {
    
    
                array[size++] = c;
            }
        }
        String newStr = new String(array, 0, size);
        return newStr;
    }
}

滑动窗口类

待补充

KMP

学习链接
核心点:最长公共前后缀的理解。KMP的主要思想是「当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。」
暴力法的问题在于:回溯的时候一个一个回溯效率太低,通过引入最长公共前后缀,即可一次移动最长公共前后缀长度,从而提升了暴力法的回溯效率。
「前缀表是用来回溯的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配,模式串应该跳到哪个位置。」

帖子链接

题目链接+KMP等算法实现版本

扫描二维码关注公众号,回复: 12909145 查看本文章

暴力法:

public class Solution {
    
    
    public int strStr(String haystack, String needle) {
    
    
        int L = needle.length();
        int n = haystack.length();
        for (int start = 0; start < n - L + 1; start++) {
    
    
	        // 回溯时,从start++的位置重新开始匹配,因此效率低
            if (haystack.substring(start, start + L).equals(needle)) {
    
    
                return start;
            }
        }
        return -1;
    }
}

KMP算法版本:

class Solution {
    
    
    public int strStr(String haystack, String needle) {
    
    
        //两种特殊情况
        if (needle.length() == 0) {
    
    
            return 0;
        }
        if (haystack.length() == 0) {
    
    
            return -1;
        }
        // char 数组
        char[] hasyarr = haystack.toCharArray();
        char[] nearr = needle.toCharArray();
        //长度
        int halen = hasyarr.length;
        int nelen = nearr.length;
        //返回下标
        return kmp(hasyarr,halen,nearr,nelen);

    }
    public int kmp (char[] hasyarr, int halen, char[] nearr, int nelen) {
    
    
        //获取next 数组
        int[] next = next(nearr,nelen);
        int j = 0;
        for (int i = 0; i < halen; ++i) {
    
    
            //发现不匹配的字符,然后根据 next 数组移动指针,移动到最大公共前后缀的,
            //前缀的后一位,和咱们移动模式串的含义相同
            while (j > 0 && hasyarr[i] != nearr[j]) {
    
    
            	// 取要与当前主串位置进行比较的下一个模式串的坐标
                j = next[j - 1] + 1;
                // 超出长度时,可以直接返回不存在。
                // 实际上是通过模式串的下标是否超过主串的长度来控制循环的,相当于一种滑窗
                if (nelen - j + i > halen) {
    
    
                    return -1;
                }
            }
            //如果相同就将i、j指针同时后移一下,比较下个字符
            if (hasyarr[i] == nearr[j]) {
    
    
                ++j;
            }
            //遍历完整个模式串,返回模式串的起点下标
            if (j == nelen) {
    
    
                return i - nelen + 1;
            }
        }
        return -1;
    }

    public  int[] next (char[] needle,int len) {
    
    
        // 定义 next 数组,实际上是一种记忆化
        // next数组记录了当前位置不匹配时包含当前字符的最长子串长度
        int[] next = new int[len];
        // 初始化,模式串从下标0开始存储,那么存储的初始值就为-1,参看帖子部分的介绍
        // 取为-1 + 1 = 0,即从第0个位置的元素开始匹配
        next[0] = -1;
        // 当前next[i]的最长前后缀长度,也是要模式串的坐标与主串当前位置比较的坐标
        int k = -1;
        for (int i = 1; i < len; ++i) {
    
    
            // 我们此时知道了 [0,i-1]的最长前后缀,但是k+1的指向的值和i不相同时,我们则需要回溯
            // 因为 next[k]就时用来记录子串的最长公共前后缀的尾坐标(即长度)
            // 就要找 k+1前一个元素在next数组里的值,即next[k+1]
            // needle[i]是当前元素,需要等于
            while (k != -1 && needle[k + 1] != needle[i]) {
    
    
                k = next[k];
            }
            // 相同情况,就是 k的下一位,和 i 相同时,此时我们已经知道 [0,i-1]的最长前后缀
            // 然后 k - 1 又和 i 相同,最长前后缀加1,即可
            if (needle[k+1] == needle[i]) {
    
    
                ++k;
            }
            next[i] = k;
        }
        return next;
    }
}

另一个KMP的例题

猜你喜欢

转载自blog.csdn.net/weixin_38370441/article/details/115164267