前言
本文是根据代码随想录中的字符串顺序进行编写,只刷了里面力扣的题 代码随想录
其他文章链接:刷算法Leetcode文章汇总
字符串篇
①双指针,前后交换
②for循环,s[i] = s[n-i-1],与双指针思想相同
java中字符串不能修改,先转换为char数组
for循环每2k个字符一组,组内使用双指针反转前k个字符,每次判断右指针是否越界
①双指针从后往前,for循环找到每个单词的左右下标,逐个将单词加入StringBuilder中
②编写char数组反转函数,先将整个字符串去掉头尾空格后反转,再使用双指针逐个找到每个单词的下标,使用反转函数将每个单词反转,然后拼接到新字符串中,并拼接一个空格
③使用双端队列Deque,双指针找到每个单词,使用头插法入队,最后用join拼接为String
class Solution {
public String reverseWords(String s) {
Deque<String> wordDeque = new ArrayDeque<>();
s = s.trim();
int l = 0, r = 0;
for(;r<s.length();r++){
if(s.charAt(r)==' '){
if(s.charAt(r-1)!=' '){
wordDeque.offerFirst(s.substring(l,r));
}
l = r+1;
}
}
wordDeque.offerFirst(s.substring(l,r));
return String.join(" ",wordDeque);
}
}
注意上面三种方法,因为空格问题,都可能需要单独处理最后一个单词。
④使用java中的库函数,trim()去除头尾空格,split("\\s+")按空格分割为数组,asList()变为ArrayList,Collections.reverse()反转list,join(" ",wordList)用空格拼接为String并返回,每个函数都可以自己手动实现
class Solution {
public String reverseWords(String s) {
s = s.trim();
List<String> wordList = Arrays.asList(s.split("\\s+"));
Collections.reverse(wordList);
return String.join(" ",wordList);
}
}
①暴力求解,双重循环判断haystack(i+j)和needle(j)
②经典KMP算法(之前学了一遍但这次还是看来好几天才看明白...),核心在于模式串pattern的next数组的构建(这里不解释原理和意义,只介绍我自己理解的next数组构建方法)
next数组有好几个版本,根据next数组后续使用的不同而不同,我采用的是next[0]=0并且next[i]记录[0,i]字符串的最长相等前后缀(即没有进行移位)
若此时要准备判断pattern[k]和pattern[i],说明已知pattern中[0,k-1]与[i-k,i-1]相等,即为pattern中[0,i-1]中最长相等前后缀,长度为k-1,next[i-1]=k-1,此时进行pattern[k]和pattern[i]的判断:
a)两字符相等,说明[0,k]与[i-k,i]相等,即next[i]=k,继续往后判断
b)两字符不等,问题重新转换为原始问题,找到pattern中[0,i]的最长相等前后缀,且前后缀长度<k,问题又转换为pattern中存在[0,k-1]的前缀与[i-k+1,i]的后缀匹配,此时换一个角度考虑,将[0,k-1]看成一个模式串,[i-k+1,i]看成一个目标串,已知pattern[k]与pattern[i]不等,根据next数组,下一个比较pattern[next[k-1]]与pattern[i]是否相等,一直重复,即有k = next[k-1]
class Solution {
public int strStr(String haystack, String needle) {
if(haystack.length() < needle.length()) return -1;
int m = haystack.length(), n = needle.length();
if(n == 0) return 0;
int[] next = new int[n];
getNext(needle, next);
for(int i = 0, j = 0; i < m; i++){
while(j > 0 && haystack.charAt(i) != needle.charAt(j)){
j = next[j-1];
}
if(haystack.charAt(i) == needle.charAt(j)) j++;
if(j == n) return i - n + 1;
}
return -1;
}
void getNext(String needle,int[] next){
for(int i = 1, k = 0; i < needle.length(); i++){
while(k > 0 && needle.charAt(i) != needle.charAt(k)){
k = next[k-1];
}
if(needle.charAt(i) == needle.charAt(k)) k++;
next[i] = k;
}
}
}
③java函数,一行代码解决,return haystack.indexOf(needle, 0);
解释为,在haystack中从下标0开始,寻找第一个与needle匹配的开始下标,否则为-1
①暴力求解,判断每种长度的字符串是否可能,使用boolean记录每次for循环中,是否出现不匹配的情况,s.charAt(j) != s.charAt(j%i)
剪枝:(a)i的范围可以缩小为[0,n/2] (b)只要出现不匹配就break
有一种判断方法为,如果s+s中存在一个为s的字串,且不是开头或者结尾,就说明s为满足题目条件的重复子字符串(Leectcode官方解答有充分性和必要性证明,这里不重复证明),根据这个方法,问题转换为判断重复子串,又有了下面三种方法:
②与28题相同,java函数一行代码return (s+s).indexOf(s,1) != s.length(),下标从1开始保证不是前一个s,返回的下标不等于s.length()保证不是后一个s
③与28题相同,使用KMP算法,详细过程不重复,next数组的构建与之前相同,在KMP匹配时,可以将haystack的下标i修改为[1,m-2],这样就保证了匹配的下标不是开头或者结尾
class Solution {
public boolean repeatedSubstringPattern(String s) {
return kmp(s+s,s) != -1;
}
public int kmp(String query, String pattern){
int m = query.length(), n = pattern.length();
int[] next = new int[n];
for(int i = 1, k = 0; i < n; i++){
while(k > 0 && pattern.charAt(i) != pattern.charAt(k)){
k = next[k-1];
}
if(pattern.charAt(i) == pattern.charAt(k)) k++;
next[i] = k;
}
for(int i = 1, j = 0; i < m - 1; i++){
while(j > 0 && query.charAt(i) != pattern.charAt(j))
j = next[j-1];
if(query.charAt(i) == pattern.charAt(j)) j++;
if(j == n) return i - n + 1;
}
return -1;
}
}
④改进KMP算法,如果s存在重复子字符串,那么对于next数组,next[n-1]>0,并且根据前面提出的判断方法,有这个 结论n%(n-next[n-1]-1)==0,详细推导见Leetcode官方最后一种解法(我这里的next数组使用的是28题中介绍的那个版本,官方的不太一样)
class Solution {
public boolean repeatedSubstringPattern(String s) {
int n = s.length();
int[] next = new int[n];
getNext(s, next);
return next[n-1] > 0 && n % (n-next[n-1]) == 0;
}
public void getNext(String pattern, int[] next){
for(int i = 1, k = 0; i < pattern.length(); i++){
while(k > 0 && pattern.charAt(i) != pattern.charAt(k)){
k = next[k-1];
}
if(pattern.charAt(i) == pattern.charAt(k)) k++;
next[i] = k;
}
}
}
字符串总结
①字符串反转问题,基本上使用双指针解决。将字符串每个字符反转,直接用双指针交换;将字符串中的单词反转,双指针用于确定每个单词的位置
②字符串单词反转问题,可以使用多次反转,先整个反转,再逐个单词反转
③子字符串匹配问题,使用暴力求解或者KMP算法,关键在于next数组的构建