题目
根据这个题目的意思,我们来做一点小改变:即给定字符串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;
}