掌握情况
| 题目 | 
通关 | 
| 反转字符串 | 
通过 | 
| 反转字符串Ⅱ | 
通过 | 
| 替换空格 | 
通过 | 
| 左旋转字符串 | 
通过 | 
| 实现 strStr() | 
未通过 | 
| 重复的子字符串 | 
未通过 | 
 
反转字符串
LeetCode 344.反转字符串
问题
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
双指针
1 2 3 4 5 6 7 8
   | public void reverseString(char[] s) {     int n = s.length;     for (int left = 0, right = n - 1; left < right; ++left, --right) {         char tmp = s[left];         s[left] = s[right];         s[right] = tmp;     } }
  | 
 
反转字符串 Ⅱ
LeetCode 541.反转字符串 II
问题
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
- 如果剩余字符少于 
k 个,则将剩余字符全部反转。 
- 如果剩余字符小于 
2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。 
算法思想
 反转每个下标从 $2k$ 的倍数开始的,长度为 $k$  的子串。若该子串长度不足  $k$  ,则反转整个子串。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | public String reverseStr(String s, int k) {     int n = s.length();     char[] arr = s.toCharArray();     for (int i = 0; i < n; i += 2 * k) {         reverse(arr, i, Math.min(i + k, n) - 1);     }     return new String(arr); }
  public void reverse(char[] arr, int left, int right) {     while (left < right) {         char temp = arr[left];         arr[left] = arr[right];         arr[right] = temp;         left++;         right--;     } }
  | 
 
替换空格
 LCR 122. 路径加密 
问题
假定一段路径记作字符串 path,其中以 “.“ 作为分隔符。现需将路径加密,加密方法为将 path 中的分隔符替换为空格 “ “,请返回加密后的字符串。 
1 2 3 4 5 6 7 8 9
   | public String pathEncryption(String path) {     char[] ch = path.toCharArray();     for (int i = 0; i < ch.length; i++) {         if (ch[i] == '.') {             ch[i] = ' ';         }     }     return new String(ch); }
  | 
 
左旋转字符串
 LCR 182. 动态口令 
问题
某公司门禁密码使用动态口令技术。初始密码为字符串 password,密码更新均遵循以下步骤:
- 设定一个正整数目标值 
target 
- 将 
password 前 target 个字符按原顺序移动至字符串末尾 
请返回更新后的密码字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | public String dynamicPassword(String password, int target) {     char[] ch = password.toCharArray();     int len = ch.length - 1;     reverse(ch, 0, target - 1);     reverse(ch, target, len);     reverse(ch, 0, len);
      return new String(ch);
  } public void reverse(char[] str, int begin, int end) {     for(int i = begin, j = end; i < j; i++, j--) {         char temp = str[i];         str[i] = str[j];         str[j] = temp;     } }
  | 
 
KMP
 28. 找出字符串中第一个匹配项的下标 
问题
 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。 
KMP
参考文章 KMP
三叶大佬的题解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
   | public int[] getNext(char[] pattern) {     if (pattern.length == 1) {         return new int[]{-1};     }     int[] next = new int[pattern.length];     next[0] = -1;     next[1] = 0;     int i = 2, j = 0;
      while (i < next.length) {         if (pattern[i - 1] == pattern[j]) {              next[i++] = ++j;         } else if (j > 0) {               j = next[j];         } else {                          next[i++] = 0;         }     }     return next; }
  public int strStr(String str, String pattern) {     if (str == null || pattern == null ||         pattern.length() < 1 || str.length() < pattern.length()) {         return -1;     }     char[] mainStr = str.toCharArray();     char[] subStr = pattern.toCharArray();     int i = 0, j = 0;     int[] next = getNext(subStr);     while (i < mainStr.length && j < subStr.length) {         if (mainStr[i] == subStr[j]) {             i++;             j++;         } else if (next[j] == -1) {                          i++;         } else {                          j = next[j];         }     }     return j == subStr.length ? i - j : -1; }
  | 
 
暴力法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | public int nativeSolution(String str, String pattern) {     int n = str.length(), m = pattern.length();     char[] s_char = str.toCharArray(), p_char = pattern.toCharArray();
           for (int i = 0; i <= n - m; i++) {                  int index = i, begin = 0;         while (begin < m && s_char[index] == p_char[begin]) {             index++;             begin++;         }                  if (begin == m) {             return i;         }     }     return -1; }
  | 
 
思考题

重复的子字符串
 459. 重复的子字符串 
问题
 给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。 
示例
1 2 3 4 5 6 7 8 9 10
   | 示例 1:
  输入: s = "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。
  示例 2:
  输入: s = "aba" 输出: false
   | 
 
枚举
算法思想
如果一个长度为 n 的字符串 s 可以由它的一个长度为 n'  的子串 s' 重复多次构成,那么:
可以从小到大枚举 n′,并对字符串 s 进行遍历,进行上述的判断。 注意到一个小优化是,因为子串至少需要重复一次,所以 n' 不会大于 n 的一半,只需要在$[1, \frac{n}{2}]$ 的范围内枚举 n'  即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | public boolean repeatedSubstringPattern(String s) {
      int n = s.length();
      for (int i = 1; i * 2 <= n; i++) {         if (n % i == 0) {             boolean match = true;             for (int j = i; j < n; j++) {                 if (s.charAt(j) != s.charAt(j - i)) {                     match = false;                     break;                 }             }             if (match) {                 return true;             }         }     }     return false; }
  | 
 
移动匹配
算法思想
当一个字符串 s:abcabc,内部由重复的子串组成,那么这个字符串的结构一定是这样的: 

那么既然前面有相同的子串,后面有相同的子串,用 s + s,这样组成的字符串中,后面的子串做前串,前面的子串做后串,就一定还能组成一个 s,如图: 

所以判断字符串 s 是否由重复子串组成,只要两个 s 拼接在一起,里面还出现一个 s,就说明是由重复子串组成。  当然,我们在判断  s + s 拼接的字符串里是否出现一个  s  的的时候,要刨除  s + s 的首字符和尾字符,这样避免在   s + s  中搜索出原来的s,我们要搜索的是中间拼接出来的   s  。 

1 2 3
   | public boolean repeatedSubstringPattern(String s) {     return (s + s).indexOf(s, 1) != s.length(); }
  | 
 
KMP
参考答案
结论
下一段复习理解 KMP,再去完成该题