算法day08-字符串篇(2)
目录
- 反转字符串中的单词
- 找出字符串中第一个匹配项的下标
- 重复的子字符串
一、反转字符串中的单词
主要思路:把整个字符串的头尾的空格先去掉,然后根据” ”来划分字符串数组,将字符串倒着放进结果中。
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]
这个前缀中,最长的相等前后缀的长度; -
构建方式是通过双指针
i
和j
,逐步扩展前后缀长度; -
若
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[]。