今天也是要刷题的一天 学算法学八股的一天
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.重复的子字符串
暴力会超时 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真的看不懂 还得再写写双指针 去看看左神的课 ! 今天也需要努力刷题