字符串包含问题:
即一个字符串是否包含另一个字符串。这个问题虽然比较简单,但是想要优化轮询的时间复杂度却不是那么的简单。
最常见的方法就是针对一个字符串的字符,一一与另一个字符串中的字符相比较,看他们是否都在其中。
即方法一: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)。