1. 程式人生 > >演算法導論 32.4-5 字串的迴圈旋轉問題

演算法導論 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;
}