目录
二、卡码网:55.右旋转字符串
一、151.翻转字符串里的单词
题目链接/文章讲解/视频讲解:代码随想录
方法一:
根据题意,解题思路如下:
- 将多余空格去除,首尾无空格,单词间有一个空格。
- 将字符串整体翻转。
- 将单词进行翻转。
1.去除多余空格
参考27.移除元素的双指针法,保留需要的字符,将不需要的字符跳过。快指针为for循环遍历,第一个if语句是寻找非空字符,第二个if语句是为了在除了第一个单词的每个单词前插入空格,while循环遍历每个单词。注意:while循环时快指针移动依靠语句中的i++而非for循环,所以最后一个单词可能溢出,需要加<length的判断。
2.翻转全部字符
while循环进行首尾交换,为了方便对单词进行翻转、方法输入了翻转的起止位置。
3.单词翻转
利用2对单词进行翻转。
class Solution {
public String reverseWords(String s) {
char[] cs = s.toCharArray();
cs = removeSpace(cs);
reverse(cs,0,cs.length-1);
reverseEachWord(cs);
return new String(cs);
}
private char[] removeSpace(char[] cs){
int slow =0;
for(int i=0 ;i<cs.length ;i++){
if(cs[i] != ' '){
if(slow != 0) cs[slow++]=' ';
while( i < cs.length && cs[i] != ' '){
cs[slow++] = cs[i++];
}
}
}
char[] newChars = new char[slow];
System.arraycopy(cs, 0, newChars, 0, slow);
return newChars;
}
public void reverse(char[] chars, int left, int right) {
if (right >= chars.length) {
System.out.println("set a wrong right");
return;
}
while (left < right) {
chars[left] ^= chars[right];
chars[right] ^= chars[left];
chars[left] ^= chars[right];
left++;
right--;
}
}
//3.单词反转
public void reverseEachWord(char[] chars) {
int start = 0;
//end <= s.length() 这里的 = ,是为了让 end 永远指向单词末尾后一个位置,这样 reverse 的实参更好设置
for (int end = 0; end <= chars.length; end++) {
// end 每次到单词末尾后的空格或串尾,开始反转单词
if (end == chars.length || chars[end] == ' ') {
reverse(chars, start, end - 1);
start = end + 1;
}
}
}
}
方法二:
分析思路:创建新的字符数组,按需要的顺序进行填充。
- 对原字符数组从尾部遍历,i为原字符数组索引,newArrPos为新字符数组索引
- 从后遍历到非空位置,记为right。再遍历到空字符,此时+1即为单词首字母
- 将单词遍历装入新字符数组,并在结尾加空格
- 最后会多一位空格,返回减少一位 new String(newArr,0,newArrPos-1)
//解法二:创建新字符数组填充。时间复杂度O(n)
class Solution {
public String reverseWords(String s) {
//源字符数组
char[] initialArr = s.toCharArray();
//新字符数组
char[] newArr = new char[initialArr.length+1];//下面循环添加"单词 ",最终末尾的空格不会返回
int newArrPos = 0;
//i来进行整体对源字符数组从后往前遍历
int i = initialArr.length-1;
while(i>=0){
while(i>=0 && initialArr[i] == ' '){i--;} //跳过空格
//此时i位置是边界或!=空格,先记录当前索引,之后的while用来确定单词的首字母的位置
int right = i;
while(i>=0 && initialArr[i] != ' '){i--;}
//指定区间单词取出(由于i为首字母的前一位,所以这里+1,),取出的每组末尾都带有一个空格
for (int j = i+1; j <= right; j++) {
newArr[newArrPos++] = initialArr[j];
if(j == right){
newArr[newArrPos++] = ' ';//空格
}
}
}
//若是原始字符串没有单词,直接返回空字符串;若是有单词,返回0-末尾空格索引前范围的字符数组(转成String返回)
if(newArrPos == 0){
return "";
}else{
return new String(newArr,0,newArrPos-1);
}
}
}
二、卡码网:55.右旋转字符串
实质上是将后n个字符与前面的字符调换位置。
方式:整体翻转,将前n个字符翻转,将剩余字符翻转即可
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,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--;
}
}
}
三、28. 实现 strStr()
题目链接/文章讲解/视频讲解:代码随想录
KMP算法:
朴素字符串匹配算法的不足
朴素算法通过两层循环,将模式串与文本串的每一个可能的起始位置进行逐字符比较。如果在比较过程中发现不匹配,模式串就会回退到起始位置,文本串指针也会移动到下一个位置重新开始比较。这种方法在最坏情况下的时间复杂度为,其中
是模式串的长度,
是文本串的长度。
KMP 算法的核心思想
KMP 算法的核心在于避免在匹配过程中模式串的不必要回退。当出现不匹配时,KMP 算法会利用已经匹配的部分信息,确定模式串应该向右移动多少位,从而继续进行匹配,而不是像朴素算法那样将模式串完全回退到起始位置。为了实现这一点,KMP 算法引入了一个重要的概念 —— 部分匹配表(Partial Match Table,也称为 next
数组)。
部分匹配表(next
数组)
部分匹配表记录了模式串中每个位置之前的子串的最长公共前后缀的长度。所谓前缀,是指除了最后一个字符以外,一个字符串的全部头部组合;后缀则是指除了第一个字符以外,一个字符串的全部尾部组合。
例如:a a b a a f 的next数组为 [0 1 0 1 2 0]
计算部分匹配表(next
数组):
- 初始化:j 是前缀字符串的尾部(也是相同字符串的长度)初始化为0,next[0]初始化为0
- 不相等的情况:i 遍历字符串,j 与 i 不相等时,j 要回退 j=next [j-1]。因为可能多次不相等要用while循环。j可能回退到0位置,所以条件有 j>0
- 相等的情况:相等时 j++ 并赋给 next[ i ]。
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( haystack.charAt(i) == needle.charAt(j)) j++;
if( j == needle.length() ) return i-needle.length()+1;
}
return -1;
}
private void getNext(int[] next , String s){
next[0] = 0;
int j = 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)) next[i] = ++j;
}
}
}
四、459.重复的子字符串(跳过)
题目链接/文章讲解/视频讲解: 代码随想录