【演算法】字串迴圈移位後是否包含
問題
給定兩個字串s1和s2,要求判斷s2是否能夠被通過s1做迴圈移位(rotate)得到的字串包含。
例如,s1=AABCD和s2=CDAA,返回true;給定s1=ABCD和s2=ACBD,返回false。
解法一
最直接最笨的方法就對s1進行迴圈移動,再進行字串包含的判斷,從而遍歷其所有的可能性。字串迴圈移動,時間複雜度為O(n),字串包含判斷,採用普通的方法,時間複雜度為O(n*m),總體複雜度為O(n*n*m)。字串包含判斷,若採用KMP演算法,時間複雜度為O(n),這樣總體的複雜度為O(n*n)。若字串的長度n較大,顯然效率比較低。其中n為s1的長度,m為s2的長度。
解法二
對s1的迴圈移位操作,可以使用拼接s1+s1的方式得到。
以S1 = ABCD為例,先分析對S1進行迴圈移位之後的結果,如下所示:
ABCD—>BCDA—->CDAB—->DABC—->ABCD……
假設我們把前面的移走的資料進行保留,會發現有如下的規律:
ABCD—>ABCDA—->ABCDAB—->ABCDABC—->ABCDABCD……
因此,可以看出對s1做迴圈移位所得到的字串都將是字串s1s1的子字串。如果s2可以由s1迴圈移位得到,那麼s2一定在s1s1上,這樣時間複雜度就降低了。
/**
* 解法二,拼接字串s1+s1
* 如果s2可以由s1迴圈移位得到,那麼s2一定在s1s1上
*/
public static boolean isContainsByConcat(String a, String b){
if(a == null || b == null || a.isEmpty() || b.isEmpty()) return false;
String newStr = a.concat(a);
return newStr.contains(b);
}
解法三
使用指標迴圈。我們的想法是,在s1後面”虛擬”地接上一個s1,這個”虛擬的s1”並不佔空間,但是仍然按照解法2的思路進行。那麼,如何實現這個”虛擬的s1”呢?其實只要把s1的最後一個元素,再指回s1的第一個元素即可。這可以用取模運算實現。比如,元素s1[(d1+i) mod d1]其實就是那個“虛擬的s1”的第i個元素,不會超出字串s1的長度。
用一個指標標記遍歷s1,最多兩遍,與s2中的第一個字元比對,如果命中則進入s2,進行逐位匹配,匹配失敗的話跳出s2繼續查詢s1。
/**
* 解法三:指標迴圈查詢
*/
public static boolean isContainsByPointer(String a, String b){
if(a == null || b == null || a.isEmpty() || b.isEmpty()) return false;
int p = 0; //s1的指標
int aLen = a.length();
int bLen = b.length();
for(int i = 0; i < aLen * 2; i++){
char c1 = a.charAt(p % aLen);
//匹配到第一個字元相同後,遍歷s2字串
if(c1 == b.charAt(0)){
for(int j = 0; j < bLen; j++){
if(a.charAt((p + j) % aLen) != b.charAt(j)){
break; //本次不完全匹配,繼續往後
}
}
return true;
}
p++;
}
return false;
}
優點:
- 字串長度較大時,效率依然較好;
- 不需要申請額外空間儲存第二個s1