算法day08-字符串篇(2)

目录

  1. 反转字符串中的单词
  2. 找出字符串中第一个匹配项的下标
  3. 重复的子字符串

一、反转字符串中的单词

   主要思路:把整个字符串的头尾的空格先去掉,然后根据” ”来划分字符串数组,将字符串倒着放进结果中。

class Solution {
    public String reverseWords(String s) {
        String[] parts = s.trim().split("\\s+");        //去掉首尾空格
        StringBuilder sb = new StringBuilder();
        for(int i = parts.length-1; i>=0; i--){
            if(i == 0){
                sb.append(parts[i]);
            }else{
                sb.append(parts[i]);
                sb.append(" ");
            }
        }
        return sb.toString();
    }
}
//时间复杂度:O(n)
//空间复杂度:O(n)

 

二、右旋转字符串

 

class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        in.nextLine();
        String s = in.nextLine();
        int p1 = s.length() - n;        //要右旋的起点
        int p2 = 0;
        StringBuilder sb = new StringBuilder();
        while(p1 < s.length()){
            sb.append(s.charAt(p1));
            p1++;
        }
        while(p2 < s.length() - n){
            sb.append(s.charAt(p2));
            p2++;
        }
        System.out.print(sb);
    }
}
//时间复杂度:O(N)
//空间复杂度:O(N)

 

三、找出字符串中第一个匹配项的下标

   主要思路:在原字符串的每一个位置都截取一段子字符串来判断是否和needle相等。

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.length()==0){
            return 0;
        }
        if(needle.length() > haystack.length()){
            return -1;
        }
        for(int i=0; i<=haystack.length() - needle.length(); i++){
            if(haystack.substring(i, i+needle.length()).equals(needle)){
                return i;
            }
        }
        return -1;
    }
}
//时间复杂度:O((n-m+1)*m)
//空间复杂度:O(1)

  KMP做法:

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.length() == 0){
            return 0;
        }
        int[] next = new int[needle.length()];
        getNext(next, 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(needle.charAt(j) == haystack.charAt(i)){
                j++;            //继续往后面匹配
            }
            if(j == needle.length()){
                return i - needle.length() + 1;
            }
        }
        return -1;
    }

    public void getNext(int[] next, String s){
        int j = 0;      //前缀的起点
        next[0] = 0;
        for(int i=1; i<s.length(); i++){
            while(j>0 && s.charAt(j) != s.charAt(i)){
                j = next[j-1];
            }
            if(s.charAt(j) == s.charAt(i)){
                j++;
            }
            next[i] = j;
        }
    }
}

 

四、重复的子字符串

  实现:

  • 构建 KMP 的前缀表(next 数组):

    • next[i] 表示 s[0..i] 这个前缀中,最长的相等前后缀的长度

    • 构建方式是通过双指针 ij,逐步扩展前后缀长度;

    • s[i] == s[j],说明可以扩展,令 j++

    • 若不等,退回到上一个最长匹配长度:j = next[j - 1],直到匹配或回退到 0。

  • 判断是否由重复子串构成:

    • 如果字符串有非零的最长相等前后缀(即 next[n - 1] > 0),并且整个字符串的长度 n 能被 n - next[n - 1] 整除;

    • 说明整个字符串可以由一个长度为 n - next[n - 1] 的子串重复拼接组成;

    • 返回 true,否则返回 false

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int n = s.length();
        int[] next = new int[n];
        int j = 0;
        next[0] = 0;
        for(int i=1; i<n; 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;
        }

        if (next[n - 1] > 0 && n % (n - next[n - 1]) == 0) { // 当字符串s的长度可以被其最长相等前后缀不包含的子串的长度整除时
            return true;
        }else{
            return false;
        }
    }
}

// 时间复杂度:O(n),其中 n 是字符串的长度,KMP 构造 next 数组只需要一次线性扫描。
// 空间复杂度:O(n),用于存储前缀表 next[]。

 

posted @ 2025-05-02 23:07  筱倩  阅读(221)  评论(0)    收藏  举报