演算法導論 32.4-5 字串的迴圈旋轉問題
題目
根據這個題目的意思,我們來做一點小改變:即給定字串s1和s2(長度分別為n,m),判斷s2是否是s1的一次移位而生成的字串的子串。之所以這樣改變,是因為在這個問題的解法中,第二個高效解法可以很好的解決本題目。因而現在,我們只討論這個改變後的問題。
解法一:直接法,暴力破解。時間複雜度O(n(m+n))
1、對s1進行一次迴圈移位;
2、判斷s2是否是s1的子串,這裡的判斷可以採用KMP演算法;
3、迴圈上述步驟,直到s2使其的子串,返回真;或者迴圈n次後,最終返回假,表示不存在這種情況。
解法二:該解法是基於這樣的一個事實:
假設s1 = "abcd",s2 = "acbd":首先對s1迴圈移位(假設右移)可得:
abcd -> dabc -> cdab -> bcda -> abcd ->....
現在,我們把每次右邊移走的資料保留,.之後為保留的已經被移走的資料,如下:
abcd -> dabc.d -> cdab.cd -> bcda.bcd -> abcd.abcd。
可以發現由s1移位所得到的所有字串都是s1s1這個組合字串的字串,因此如果s2出現在s1的某一個移位後的字串裡面,那麼s2必定在s1s1上,利用這個事實,我們可以得到一個很高效的演算法,實際上就是KMP演算法。本質上其實是空間換時間,複雜度為O(m+n)。
解法二和演算法導論 32.4-5的關係
回到32.4-5這個題目,判斷兩個字串S1和S2(其中length[S1] = length[S2])是否互為迴圈旋轉,只需兩次運用KMP演算法即可:
1、在S1S1中查詢是否存在S2;
2、在S2S2中查詢是否存在S1;
3、若兩次均返回真,則說明是彼此的迴圈旋轉。
最後,給出關於上述改編問題的程式碼:
#include<string> #include<iostream> using namespace std; class string_rotation {//字串移位,迴圈右移,左移類似.//判斷s2是否是s1的某一個移位後的字串 private: string s1; string s2; public: string_rotation(const string &s11, const string &s22) :s1(move(s11)), s2(move(s22)){} bool matching(); bool effective_solution(); }; bool string_rotation::matching() {//解法一:樸素解法。總時間複雜度O(n(m + n)),其中n和m分別為s1和s2的長度 size_t n = s1.size(); for (size_t i = 0; i != n; ++i) {//最多移位n次 char temp = s1[n - 1]; for (int j = n - 2; j >= 0; --j) s1[j + 1] = s1[j]; s1[0] = temp; //這個地方直接呼叫string的成員函式查詢字串在s1中是否出現 //也可以自己寫一個字串匹配演算法,比如KMP,find演算法也許就是利用KMP實現的 //呢,下同。時間為O(m + n) if (s1.find(s2) != string::npos) return true; } return false; } bool string_rotation::effective_solution() {//高效解法,實為KMP s1 = s1.append(s1); if (s1.find(s2) != string::npos)//此處find演算法可以用KMP代替,我想find演算法的時間和KMP差不多吧 return true; return false; } int main() { string s1 = "aabcd", s2 = "cdaa"; string_rotation sr(s1, s2); cout << boolalpha << sr.matching() << endl; cout << boolalpha << sr.effective_solution() << endl; system("pause"); return 0; }