算法导论 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;
}




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值