读程序员编程艺术第二章---字符串包含问题

字符串包含问题:

即一个字符串是否包含另一个字符串。这个问题虽然比较简单,但是想要优化轮询的时间复杂度却不是那么的简单。

最常见的方法就是针对一个字符串的字符,一一与另一个字符串中的字符相比较,看他们是否都在其中。

即方法一:O(n*m)的轮询方法,由于这种方法比较简单,代码我就不贴了。

    方法二:基于O(mlogm)+O(nlogn)+O(m+n)的排序方法 的轮询,下面重点介绍这种方法。

我们知道如果首先排序两个字符串,在同时针对两个字符串轮询降低时间复杂度。而快速排序的时间复杂度为O(nlogn)最后的线性扫描也需要O(m+n)次操作。故最后的时间复杂度为O(mlogm)+O(nlogn)+O(m+n)。


首先我们来看一下快速排序算法。例如要将4,3.5,6,7,8,9,10,2排序。


代码编写如下:

//分界函数
int partrition(int s[],int l,int r)
{
	//i指向起始元素,j指向结束元素
	int i=l;int j=r;
	//设定起始元素为轴
	int key=s[l];
	//当i<j时,先从左往右找比key小的数填充s[i],
	//再从右往左找比key大的数填充刚刚挖出的s[j]
	while(i<j)
	{
		//先从左往右找比key小的数填充s[i]
		while(i<j&&s[j]>=key)
		{
			j--;
		}
		if(i<j)
		{
			s[i]=s[j];
			i++;
		}
		//再从右往左找比key大的数填充刚刚挖出的s[j]
		while(i<j&&s[i]<key)
		{
			i++;
		}
		if(i<j)
		{
			s[j]=s[i];
			j--;
		}
	}

	//将key填充回s[i]
	s[i]=key;
	//返回下次的轴值
	return i;
}


//分治代码
void Quick_sort(int s[],int l,int r)
{
	//递归出口
	if(l<r)
	{
		int t=partrition(s,l,r);
		//将轴的左边排序
		Quick_sort(s,l,t-1);
		//将轴的右边排序
		Quick_sort(s,t+1,r);
	}
}

总结:快速排序采用的是分治的策略

1、将起始位置的数x设置为轴,定义指针i,j分别指向最开始和最末尾的元素

2、从右往左寻找比x小的数,如果满足,就将s[i]用s[j]填充并将i++,这时相应位置的s[j]空出

3、从左往右寻找比x大的数,如果满足,就将s[j](就是刚刚空出的s[j])用s[i]填充,此时相应的s[i]又会空出

4、如果此时i<j满足,重复2、3。否则执行5

5、将key值填回s[i]

6、返回轴


好了上面简单的介绍了快速排序算法。但是我们现在要排序是字符串,我们将快速排序算法稍加改动,编写代码如下:

//分界函数
int partrition(string &s,int l,int r)
{
	//i指向起始元素,j指向结束元素
	int i=l;int j=r;
	//设定起始元素为轴
	int key=s[l];
	//当i<j时,先从左往右找比key小的数填充s[i],
	//再从右往左找比key大的数填充刚刚挖出的s[j]
	while(i<j)
	{
		//先从左往右找比key小的数填充s[i]
		while(i<j&&s[j]>=key)
		{
			j--;
		}
		if(i<j)
		{
			s[i]=s[j];
			i++;
		}
		//再从右往左找比key大的数填充刚刚挖出的s[j]
		while(i<j&&s[i]<key)
		{
			i++;
		}
		if(i<j)
		{
			s[j]=s[i];
			j--;
		}
	}

	//将s[i]填充回key
	s[i]=key;
	//返回下次的轴值
	return i;
}


//分治代码
void Quick_sort(string &s,int l,int r)
{
	//递归出口
	if(l<r)
	{
		int t=partrition(s,l,r);
		//将轴的左边排序
		Quick_sort(s,l,t-1);
		//将轴的右边排序
		Quick_sort(s,t+1,r);
	}
}

判断字符串是否包含的完整代码编写如下:

#include"stdafx.h"
#include<stdio.h>
#include"string"
#include"iostream"
using namespace std;


//分界函数
int partrition(string &s,int l,int r)
{
	//i指向起始元素,j指向结束元素
	int i=l;int j=r;
	//设定起始元素为轴
	int key=s[l];
	//当i<j时,先从左往右找比key小的数填充s[i],
	//再从右往左找比key大的数填充刚刚挖出的s[j]
	while(i<j)
	{
		//先从左往右找比key小的数填充s[i]
		while(i<j&&s[j]>=key)
		{
			j--;
		}
		if(i<j)
		{
			s[i]=s[j];
			i++;
		}
		//再从右往左找比key大的数填充刚刚挖出的s[j]
		while(i<j&&s[i]<key)
		{
			i++;
		}
		if(i<j)
		{
			s[j]=s[i];
			j--;
		}
	}

	//将s[i]填充回key
	s[i]=key;
	//返回下次的轴值
	return i;
}


//分治代码
void Quick_sort(string &s,int l,int r)
{
	//递归出口
	if(l<r)
	{
		int t=partrition(s,l,r);
		//将轴的左边排序
		Quick_sort(s,l,t-1);
		//将轴的右边排序
		Quick_sort(s,t+1,r);
	}
}


void compare(string str1,string str2)
{
	int posOne=0;
	int posTwo=0;
	while(posTwo<str2.length()&&posOne<str1.length())
	{
		while(str1[posOne]<str2[posTwo]&&posOne<str1.length()-1)
			posOne++;
		if(str1[posOne]!=str2[posTwo])
			break;
		//这儿的posOne不能自增,为什么呢,因为,如果str2中如果有重复的字符,
		//而在str1中只有一个字符与之相对应
		//这时会出现一个bug
		posTwo++;
	}

	if(posTwo==str2.length())
		cout<<"true"<<endl;
	else
		cout<<"false"<<endl;
}



int main()
{
	string s1="cdefghab";
	string s2="habm";
	Quick_sort(s1,0,s1.length()-1);
	Quick_sort(s2,0,s2.length()-1);
	compare(s1,s2);
	return 0;
}

从上面可以看出,快速排序的时间复杂度分别是O(nlogn)+O(mlogm),而线性扫描的时间复杂度为0(m+n)。如此,总的时间复杂度为O(nlogn)+O(mlogm)+0(m+n)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值