一、概要
本文介紹了有關字串的演算法第一部分的Java
程式碼實現,所有程式碼均可通過 線上編譯器 直接執行,演算法目錄:
- 替換字串中的空格
- 輸入一個字串,列印出該字串的所有排列
- 第一個只出現一次的字元
- 翻轉句子
- 計算字串之間的最短距離
二、程式碼實現
2.1 替換字串中的空格
問題描述
實現一個函式,將字串p
中的所有空格都替換成為指定的字串r
。
解決思路
- 遍歷原始的字串
p
,統計原先字串中空格的個數spaceNum
。 - 建立一個新的陣列
n
,用於存放替換後的字串。由於原先字串中空格也佔了一個位置,因此新陣列n
的長度為p.len + (r.len - 1) * spaceNum
。 - 對於
p
從後往前遍歷,如果 遇到了空格,那麼就將需要替換的字串r
中的字元 從後往前 填入n
陣列中;如果 遇到了非空格,那麼就將p
中的字元填入n
陣列中。
實現程式碼
class Untitled {
static char[] replaceSpace(char p[], char r[], int pLen, int rLen){
int spaceNum = 0;
int i;
for(i = 0; i < pLen; i++){
if(p[i] == ' ')
spaceNum += (rLen-1);
}
int nLen = pLen+spaceNum;
char n[] = new char[nLen+1];
i = nLen-1;
int j = pLen-1;
while(i >= 0 && j >= 0){
if (p[j] == ' ') {
for (int k = rLen-1; k >= 0; k--)
n[i--] = r[k];
} else {
n[i--] = p[j];
}
j--;
}
n[nLen] = 0;
return n;
}
public static void main(String[] args) {
char[] source = "I am sentence with space".toCharArray();
char[] replace = "%20".toCharArray();
char[] result = replaceSpace(source, replace, source.length, replace.length);
System.out.println(result);
}
}
複製程式碼
執行結果
>> I%20am%20sentence%20with%20space
複製程式碼
2.2 輸入一個字串,列印出該字串的所有排列
問題描述
輸入一個字串,列印出該字串中字元的所有排列。例如輸入字串abc
,則輸出由字元a
、b
、c
所能排列出來的所有字串abc
、acb
、bac
、bca
、cab
和cba
。
解決思路
這是一個 遞迴問題,求一個長度為n
的字串的全排列的方法為:
n[0..n.len-1]
全排列的計算方法為:將n[0]
位置的字元分別和n[1..n.len-1]
的每一個字串交換,n[0]
和交換後的n[1..n.len - 1]
的全排列進行組合。我們將字串{s}
的全排列表示為{s}
,那麼對於abc
來說,其全排列{abc}
,就等於是a + {bc}
、b + {ac}
,c + {ba}
。- 以此類推,
n[1..n.len - 1]
的全排列,則是將n[1]
分別和n[2..n.len - 1]
的每一個字串交換,再求出交換後的n[2..len - 1]
的全排列,遞迴結束的條件為n[i..n.len - 1]
只有一個字元,例如,bc
的全排列為b + {c}
、c + {b}
,而{c}
和{b}
的全排列只有一種,因此遞迴結束,這時候就可以列印出結果。
實現程式碼
class Untitled {
static void permutationStr(char p[], int depth, int length){
if (depth == length) {
System.out.println(p);
return;
}
char c;
for (int i = depth; i < length; i++){
c = p[depth]; p[depth] = p[i]; p[i] = c;
permutationStr(p, depth+1, length);
c = p[depth]; p[depth] = p[i]; p[i] = c;
}
}
public static void main(String[] args) {
char[] source = "abc".toCharArray();
permutationStr(source, 0, source.length);
}
}
複製程式碼
執行結果
>> abc
>> acb
>> bac
>> bca
>> cba
>> cab
複製程式碼
2.3 第一個只出現一次的字元
問題描述
在字串中找出第一個只出現一次的字元。如輸入abaccdeff
,則輸出b
,要求時間複雜度為O(n)
。
解決思路
這裡需要採用 以空間換時間 的思想,也就是建立一個足夠大的陣列c
,這裡為256
,然後對原始的陣列p
進行兩次遍歷:
- 第一次 從頭開始 遍歷
p
,以p
的值作為陣列c
的下標,並將c
中對應位置的值加1
,也就是說c[Integer.valueOf(i)]
的值表示的是字元i
在p
中出現的次數。這和HashMap
的原理有些類似,只不過是將查詢的key
值直接簡化成為了value
的整型值。 - 第二次 從頭開始 遍歷
p
,查詢陣列c
對應位置該值是否為1
,如果為1
,那麼就表示它是第一次只出現一次的字元。
實現程式碼
class Untitled {
static char firstNotRepeat(char p[], int len){
if (len == 1)
return p[0];
int c[] = new int[256];
int i;
char r = p[0];
for (i = 0; i < 256; i++)
c[i] = 0;
for (i = 0; i < len; i++)
c[p[i]] += 1;
for (i = 0; i < len; i++) {
if (c[p[i]] == 1) {
r = p[i];
break;
}
}
return r;
}
public static void main(String[] args) {
char[] source = "abaccdeff".toCharArray();
char c = firstNotRepeat(source, source.length);
System.out.println(c);
}
}
複製程式碼
執行結果
>> b
複製程式碼
2.4 翻轉句子
問題描述
翻轉句子中單詞的順序,但單詞內字元的順序不變,句子中單詞以空格符隔開。例如I am a original string
翻轉後的結果為string original a am I
。
解決思路
實現過程分為兩步:
- 第一步,將整個句子中的所有字元都翻轉
- 第二步,遍歷翻轉後的句子,對於句子內的每一個單詞,將其字元再翻轉一次,就能保證單詞內字元的順序不變。翻轉單詞的時候,通過
pStart
和pEnd
記錄每次遇到單詞的起止下標,並使用子方法reverseSub
對單詞中的字元進行翻轉。
實現程式碼
class Untitled {
static void reverseSub(char p[], int start, int end){
char c;
int i = start;
int j = end;
while(i < j){
c = p[i]; p[i] = p[j]; p[j] = c;
i++; j--;
}
}
static void reverseSentence(char p[], int length){
//首先翻轉整個具體的所有字元。
reverseSub(p, 0, length-1);
int pStart = 0;
int pEnd = 0;
//從頭開始遍歷,尋找句子中的單詞,pStart和pEnd分別表示單詞的起止下標。
while(pStart < length && pEnd < length){
if(p[pStart] == ' '){
pStart++;
pEnd++;
} else if (p[pEnd] == ' ' || p[pEnd] == '\0') {
//翻轉單詞中的字元。
reverseSub(p, pStart, --pEnd);
pStart = ++pEnd;
} else {
pEnd++;
}
}
}
public static void main(String[] args) {
char[] source = "I am a original string".toCharArray();
System.out.println(source);
reverseSentence(source, source.length);
System.out.println(source);
}
}
複製程式碼
執行結果為:
>> string original a am I
複製程式碼
2.5 計算字串之間的最短距離
問題描述
假設我們有兩個字串A
和B
,那麼如果想要將字串A
通過以下三種操作變換成B
:刪除、新增和修改,操作步驟的次數就稱為 字串 A 和 B 之間的距離。
現在給定兩個字串,求這兩個字串之間的最短距離。
解決思路
首先,我們需要先明確一個前提條件:如果A
的長度為0
,那麼A
和B
之間的距離就為B
的長度,反之對於B
也如此。
下面,我們在來看普通的情況,假如A[0]
和B[0]
相同,那麼A
和B
之間的距離就為A[1..A.len-1]
和B[1..B.len-1]
之間的距離;假如A[0]
和B[0]
不相同,那麼想要讓A
和B
相同,執行的操作有以下幾種:
- 刪除
A
的第一個字元,然後計算A[1..A.len-1]
和B[0..B.len-1]
的距離 - 刪除
B
的第一個字元,然後計算A[0..A.len-1]
和B[1..B.len-1]
的距離 - 修改
A
的第一個字元為B
的第一個字元,然後計算A[1..A.len-1]
和B[1..B.len-1]
的距離 - 修改
B
的第一個字元為A
的第一個字元,然後計算A[1..A.len-1]
和B[1..B.len-1]
的距離 - 增加
A
的第一個字元到B
第一個字元之前,然後計算A[1..A.len-1]
和B[0...B.len-1]
的距離 - 增加
B
的第一個字元到A
第一個字元之前,然後計算A[0...A,len-1]
和B[1..B.len-1]
的距離
對於以上這六種情況,其實最終都可以歸納為 經過一次操作,再加上剩下部分的操作次數,那麼我們的接下來的工作就是 求出剩下部分的操作部分的最小值。對於上面的任意一種情況,經過劃分後A
和B
的長度都會減少,那麼最終必然會達到我們在一開始談到的 前提條件:如果A
的長度為0
,那麼A
和B
之間的距離就為B
的長度,反之對於B
也如此。
實現程式碼
class Untitled {
static int minValue(int t1, int t2, int t3){
if (t1 < t2) {
return t1 < t3 ? t1 : t3;
} else {
return t2 < t3 ? t2 : t3;
}
}
static int calStringDis(char p1[], char p2[], int p1Start, int p2Start, int p1Len, int p2Len){
if (p1Len == 0) {
if (p2Len == 0)
return 0;
else
return p2Len;
}
if (p2Len == 0) {
if (p1Len == 0)
return 0;
else
return p1Len;
}
if (p1[p1Start] == p2[p2Start])
//A和B的第一個字元相同。
return calStringDis(p1, p2, p1Start+1, p2Start+1, p1Len-1, p2Len-1);
else {
//(1) 刪除B的第一個字元,或者將B的第一個字元放到A之前。
int t1 = calStringDis(p1, p2, p1Start, p2Start+1, p1Len, p2Len-1);
//(2) 刪除A的第一個字元,或者將A的第一個字元放到B之前。
int t2 = calStringDis(p1, p2, p1Start+1, p2Start, p1Len-1, p2Len);
//(3) 修改A的第一個字元為B的第一個字元,或者修改B的第一個字元為A的第一個字元。
int t3 = calStringDis(p1, p2, p1Start+1, p2Start+1, p1Len-1, p2Len-1);
//計算以上三種情況的最小值,再加上這次操作的次數。
return minValue(t1, t2, t3) + 1;
}
}
public static void main(String[] args) {
char[] source = "abcde".toCharArray();
char[] other = "bcd".toCharArray();
System.out.println("" + calStringDis(source, other, 0, 0, source.length, other.length));
}
}
複製程式碼
執行結果
>> 2
複製程式碼
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
- 個人主頁:lizejun.cn
- 個人知識總結目錄:lizejun.cn/categories/