今后改用java敲代码。
一、反转字符串
(1)通过双指针,left指向头,right指向尾,相互交换即可。过于简单不阐述了。
二、反转字符串2(力扣541)
(1)题目描绘:给定一个字符串 s
和一个整数 k
,从字符串开头算起,每计数至 2k
个字符,就反转这 2k
字符中的前 k
个字符。
- 如果剩余字符少于
k
个,则将剩余字符全部反转。 - 如果剩余字符小于
2k
但大于或等于k
个,则反转前k
个字符,其余字符保持原样。
(2)解题思路:可以理解为将字符串切割成小字符串然后使用一反转字符串,i每次截取2k个字符,再不同情况确定需要反转的区域。
1.如果剩余字符少于 k
个,则将剩余字符全部反转。这时候right指针应该指向结尾s.length()-1
2.如果剩余字符小于 2k
但大于或等于 k
个,则反转前 k
个字符,其余字符保持原样。以及正常情况right都是i+k-1;
(3)整个过程中java字符串中的字符修改是一个难点。
这里提供2个方法
1.首先是通过将string字符串转换成字符数组。
char[] arr = s.toCharArray();arr可以直接使用字符下标修改。
2.通过您还可以使用StringBuilder类来对字符串进行处理
String test = "chaojimali;";
StringBuilder sb = new StringBuilder(test);
sb.replace(test.length() - 1, test.length(), ""); // 这里将最后的;去除,第一个参数是需要修改字符串的开头,第二个是要截取的结尾,最后一个参数是要替换的字符。
test = sb.toString();
三、反转字符里的单词(力扣151)
1.题目描述:给你一个字符串 s
,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s
中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s
中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = "the sky is blue
输出:"blue is sky the
2.算法描述:从后往前截取单词,拼接在前面。
class Solution {
public String reverseWords(String s) {
s = ' ' + s;
int left = s.length()-1;
int right = left;
StringBuilder ans = new StringBuilder();//要修改需要StringBuilder
while(left >= 0) {
char ch = s.charAt(right);
if (ch == ' ') {
left--;right--;
}//避免连续空格,只要保证最右边的不能是空格。这样肯定是单词开头。
else if(s.charAt(left) != ' ')left--;
else {ans.append(s, left + 1, right+1);//将s中的left+1到right的字符串截取拼接到ans后面。
ans.append(' ');right = left;}
}
return ans.substring(0, ans.length() - 1);
}
}
四、KMP算法(28)
(1)KMP主要解决的是模式匹配,也就是在字符串中找到对应子串的字符下标。
日常中暴力算法是将主串遍历,不断与子串对比,如果与子串不同,主串指针要回到第二个 字符重新进行对比,时间复杂度为O(n*m);
KMP与暴力算法不同的是,当主串与子串不同时,不用重新开始对比,而是寻找指向主串 的指针要与子串的哪个对比,这样主串的指针就不会回退,时间复杂度就为(n+m)
(2)KMP算法主要要解决2个问题:1.求next数组 2.模式匹配
1.求next数组,next数组的值为该点前后缀相同最长的字符串。
a | a | b | a | a | f | |
0 | 1 | 0 | 1 | 2 | 0 |
以图中红色为例,aabaa,next数组为2,这个2是说明前面aa和后面aa相同,代表着2个字符的最长字符串相同,同样对于f来说,下一个需要对比的就是看f是否与next[2]相同,相同的话就是f对应的next数组直接+1,不同的话就继续寻找f需要对比的值,也就是j = next[j-1]
int[] arr = new int[needle.length()];
arr[0] = 0;
int j = 0;
int i = 1;
while (i < needle.length()) {
while (j > 0 && needle.charAt(i) != needle.charAt(j)) {
j = arr[j-1];
}//寻找需要对比的j值
if(needle.charAt(i) == needle.charAt(j)){//找到后直接赋值。
j++;
arr[i] = j;
}//一定要注意顺序,逻辑理清楚
i++;
}
2.模式匹配
int k = 0,m = 0;
while( k < haystack.length() && m < needle.length()){
if(haystack.charAt(k) == needle.charAt(m)){m++;k++;}//该字符匹配成功,匹配下一个字符串
else if(m == 0)k++;
//如果没有匹配成功且对比的是子串的第一个字符,主串指针向后移。注意这个m==0的情况,不容会造成上面的循环变为死循环。
else { while( haystack.charAt(k) != needle.charAt(m) && m !=0){
m = arr[m - 1];
}//没有匹配成功且对比的不是第一个字符,寻找下一个需要对比的字符。
}
}
if(m == needle.length()) return k - needle.length();
else return -1;
五、找到重复字符子串(力扣 459)
给定一个非空的字符串 s
,检查是否可以通过由它的一个子串重复多次构成。
思路:
假设判断字符串s(n个字符构成)是不是由重复子串构成的,我们先将两个字串拼成一个大的字符串:S = s + s(S 有2n个字符);
字符串拼接的最底层思想应该是使用字符串移位来判断字符串是否由重复子串(循环节)构成,因为S这个大的字符串 其实包含了字符串s的所有移位字符串。令字符串字符索引从0开始,[m,n]表示S中索引为m到索引为n的这一段字符,索引为闭区间,则[0,n-1]即S的前一半字符表示原始字符串s;[1,n]表示s右移1位的状态;[2,n+1]表示s右移2位的状态;······;[n,2n-1]即S的后半拉字符串可以理解为s移n位的状态,此时s已经移回了原始的状态了;
此外我们不难知道如果一个没有循环节的字符串在移位时必须要右移n次他才能移回它的原始状态,而有循环节的字符串最多只需要n/2次(只有两个循环节);所以当S砍去首尾字符时,对于没有循环节的字符串s,相当于砍去了[0,n-1]的原始状态和[n,2n-1]这个移位n次的又回归原始的状态,我们在[1,2n-2]范围内只能找到s移位1,2,···,n-1位时的状态,所以在[1,2n-2]内是不存在无循环节的s的;但是对于有循环节的s来说,n/2 <= n-1,所以一定存在至少一个移位状态为s,即最少存在一个s(其实对于有循环节的s来说,不考虑移位状态我们也能明白[1,2n-2]内一定至少有一个s,例如对于有两个循环节的s:组成S的前一个s的后一半字符 + 组成S的后一个s的前一半的字符 == s;三个循环节的s更不用说了);
class Solution {
public boolean repeatedSubstringPattern(String s) {
String str = s + s;
return str.substring(1, str.length() - 1).contains(s);
}
}