前言:周末閑來無事,看了看字符串相關算法的講解視頻,收貨頗豐,跟着視頻講解簡單做了一下筆記,方便以后翻閱復習同時也很樂意分享給大家。什么字符串在算法中有多重要之類的大路邊上的客套話就不多說了,直接上筆記吧。
一、字符串
-
java:String內置類型,不可更改。(如需更改可考慮:StringBuffer, StringBuilder,char[]等)
二、歸類
字符串涉及到的相關題型通常會是以下幾個方面:
- 概念理解:字典序
- 簡單操作:插入刪除字符、旋轉
- 規則判斷(羅馬數字轉換 是否是合法的整數、浮點數)
- 數字運算(大數加法,二進制加法)
- 排序、交換
- 字符計數:變位詞
- 匹配(正則表達式、全串匹配、KMP、周期判斷)
- 動態規划(LCS、編輯距離、最長回文子串)
- 搜索(單詞變換、排列組合)
三、例題
1、交換:把一個只包含01的串排序,可交換任意兩個數的位置,最少需要多少次交換?
思路:從兩頭往中間掃盪,掃盪過程中在左邊遇到1就和右邊遇到的0交換位置,直接到左有下標相遇時結束。 具體代碼如下:
1 public static void main(String[] strs) { 2 int count = 0; 3 int[] arrays = new int[] {0, 0, 1, 1, 1, 0, 1, 0, 0, 1}; 4 int left = 0; 5 int right = arrays.length - 1; 6 while (true) { 7 while (arrays[left] == 0) { 8 left++; 9 } 10 while (arrays[right] == 1) { 11 right--; 12 } 13 if (left >= right) { 14 break; 15 } else { 16 int temp = arrays[left]; 17 arrays[left] = arrays[right]; 18 arrays[right] = temp; 19 count++; 20 } 21 } 22 Logger.println("交換次數:" + count); 23 for (int array : arrays) { 24 Logger.print(array + ", "); 25 } 26 }
清晰起見,交換次數和排序后的的字符串輸出如下:
交換次數:3 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
2、字符串替換和復制:刪除一個字符串所有的a,並且復制所有的b(字符數組足夠大)
思路:詳細思路見代碼注釋
1 public static void main(String[] strs) { 2 char[] input = new char[]{'a', 'b', 'c', 'd', 'a', 'f', 'a', 'b', 'c', 'd', 'b', 'b', 'a', 'b'}; 3 char[] chars = new char[50]; 4 for (int j = 0; j < input.length; j++) { 5 chars[j] = input[j]; 6 } 7 Logger.println("操作前:"); 8 for (char c:chars 9 ) { 10 Logger.print(c + ", "); 11 } 12 int n = 0; 13 int countB = 0; 14 // 1、刪除a,用n當做新下標,循環遍歷數組,凡是不是a的元素都放到新下標的位置,由於新n增長慢,老下標i增長快,所以元素不會被覆蓋。 15 // 並且在刪除a時順便記錄b的數量,以便下一步復制b時可以提前確定數組最終的最大的下標。 16 for (int i = 0; chars[i] != '\u0000' && i < chars.length; i++) { 17 if (chars[i] != 'a') { 18 chars[n++] = chars[i]; 19 } 20 if (chars[i] == 'b') { 21 countB++; 22 } 23 } 24 25 // 2、復制b,由於在第一步中就已經知道了字符串中b的個數,這里就能確定最終字符串的最大下標,從最打下表開始倒着復制原字符串,碰到b時復制即可。 26 int newMaxIndex = n + countB - 1; 27 for (int k = n - 1; k >= 0; k--) { 28 chars[newMaxIndex--] = chars[k]; 29 if (chars[k] == 'b') { 30 chars[newMaxIndex--] = chars[k]; 31 } 32 } 33 34 Logger.println("\n操作后:"); 35 for (char c:chars 36 ) { 37 Logger.print(c + ", "); 38 } 39 }
3、交換星號:一個字符串只包含 * 和數字,請把它的 * 都放在開頭。
如:1 * 2 * 4 * 3 => * * * 1 2 4 3
- 方案一:倒着操作,從最大下標開始向前遍歷,遇到非 * 號的元素則加入"新"下標中,遍歷完畢后,j即代表 * 號的個數,然后將0-j賦值為 * 即可。(操作后,數字的相對位置不變) 代碼如下:
1 public static void main(String[] strs) { 2 char[] chars = new char[]{'1', '*', '4', '3', '*', '5', '*'}; 3 // 方案一(操作后,數字的相對位置不變) 4 // 倒着操作:從最大下標開始向前遍歷,遇到非*號的元素則加入"新"下標中,遍歷完畢后,j即代表*號的個數,然后將0-j賦值為*即可。 5 int j = chars.length - 1; 6 for (int i = j; i >= 0; i--) { 7 if (chars[i] != '*') { 8 chars[j--] = chars[i]; 9 } 10 } 11 while (j >= 0) { 12 chars[j--] = '*'; 13 } 14 for (char c:chars 15 ) { 16 Logger.print(c + ", "); 17 } 18 }
輸出結果如下:
*, *, *, 1, 4, 3, 5,
- 方案二(操作后,數組的相對位置會變)快排划分,根據循環不變式(每一步循環之后條件都成立):如本題[0..i-1]是*,[i..j-1]是數字,[j...n-1]未探測,循環時,隨着i和j增加,維護此條件依然不變,代碼如下:
1 public static void main(String[] strs) { 2 char[] chars = new char[]{'1', '*', '4', '3', '*', '5', '*'}; 3 // 方案二(操作后,數組的相對位置會變) 4 // 快排划分,根據循環不變式(每一步循環之后條件都成立):如本題[0..i-1]是*,[i..j-1]是數字,[j...n-1]未探測,循環時,隨着i和j增加,維護此條件依然不變 5 for (int i = 0, j = 0; j < chars.length; ++j) { 6 if (chars[j] == '*') { 7 char temp = chars[i]; 8 chars[i] = chars[j]; 9 chars[j] = temp; 10 i++; 11 } 12 } 13 for (char c:chars 14 ) { 15 Logger.print(c + ", "); 16 } 17 }
輸出結果如下:
*, *, *, 3, 1, 5, 4,
4、單詞翻轉
例如:I am a student =》 student a am I
思路:
1、先將整個字符串翻轉:如:I am a student =》 tneduts a ma I
2、通過空格判斷出每個單詞,然后對每個單詞進行翻轉
代碼如下:
1 public static void main(String[] strs) { 2 String input = "I am a student"; 3 char[] chars = input.toCharArray(); 4 int i = 0; 5 int j = chars.length - 1; 6 while (i < j) { 7 swap(chars, i++, j--); 8 } 9 int front = 1; 10 int tail = 0; 11 while (front < chars.length) { 12 if (chars[front] == ' ') { 13 int frontTemp = front - 1; 14 while (tail < frontTemp) { 15 swap(chars, tail++, frontTemp--); 16 } 17 tail = front + 1; 18 } 19 front++; 20 } 21 for (char c:chars 22 ) { 23 Logger.print(c); 24 } 25 } 26 27 public static void swap(char[] chars, int index1, int index2) { 28 char temp = chars[index1]; 29 chars[index1] = chars[index2]; 30 chars[index2] = temp; 31 }
輸出結果如下:
student a am I
5、子串變位詞:給定兩個串a和b,問b是否a的子串變位詞。
例如:a=hello。b=lel,lle,ello都是true;b=elo是false
思路:
-
- 一、首先需要了解對兩個串是否是變位詞的判斷:
-
- 對兩個串按統一規則排序,排序后若相等則是變位詞。
- 對兩個串中出現的字母統計,兩串中相同的字母出現的次數一致則為變位詞。
-
- 二、然后從母串的第一個元素遍歷,每往后遍歷一個元素就把從當前元素開始到加上子串的長度的位置作為一個區間和子串比較是否是變位詞。
最后一題綜合前幾個題用到的一些技巧,還是比較有趣的,這里就不貼出代碼了,也激發一下大家的動手能力,感興趣的童鞋不妨試着寫一寫。
以上問題如有不同思路歡迎交流。