您的位置:首页 > 职场人生

【July程序员编程艺术】之字符串是否包含问题

2015-10-09 17:59 656 查看
题目描述:

假设这有一个各种字母组成的字符串A,和另外一个字符串B,字符串里B的字母数相对少一些。什么方法能最快的查出所有小字符串B里的字母在大字符串A里都有?

比如,如果是下面两个字符串:

String 1: ABCDEFGHLMNOPQRS

String 2: DCGSRQPO

答案是true,所有在string2里的字母string1也都有。

如果是下面两个字符串:

String 1: ABCDEFGHLMNOPQRS

String 2: DCGSRQPZ

答案是false,因为第二个字符串里的Z字母不在第一个字符串里

我的解题思路:

1.最笨的方法

任何好的方法都是将笨方法不断改进得到,因此拿到这个题目第一反应就是笨方法。对于String 2中的每一个字符,都拿到String 1中挨个找一下。这样写出来的程序是两层遍历,假设长字符串长度为n,短字符串长度为m,那么这种算法的时间复杂度是O(mn)。肯定是不好的。

2.初步改进的思路

思考如何在笨方法的基础上进行改进。笨方法中string1中的字符被重复遍历了m次,这无疑是冗余和可以优化的地方。要想减少遍历string1的次数,那么连续的两次遍历之间需要有联系才行。于是就想到了能不能先将字符串排个序,这样第一次遍历的结果就可以为第二次遍历提供帮助了。说到排序,最容易想到的自然就是快速排序算法,即July博客中所讲到的方法1.2。编写程序如下:

#include "quick_sort.h"
#include <iostream>
#include <string>
using namespace std;
template <class T>
//int src[]={72,6,57,88,60,42,83,73,48,85};
int partition(T * src,T * end)
{
T * sp=src; //起始位置指针
T * ep=end; //结束位置指针
T tmp;
T * cp=src;
while(ep>sp)
{
while((*ep>=*cp)&&(ep>cp))
ep--;
if(ep<=cp)
break;
swap<T>(cp,ep);
cp=ep;
while((*sp<=*cp)&&(sp<cp))
sp++;
if(sp>=cp)
break;
swap<T>(cp,sp);
cp=sp;

}
int res = cp-src;
return res;
}

template <class T>
void swap(T * a,T * b )
{
T tmp;
tmp=*a;
*a=*b;
*b=tmp;
}

template <class T>
void quick_sort(T *src,T * end)
{
if(end>src)
{
int q = partition(src,end);
quick_sort(src,src+q-1);
quick_sort(src+q+1,end);
}
}

int compare(string & l_str,string & s_str)
{
char * lp,*sp,*l_end,*s_end;
lp=&(l_str[0]);
sp=&(s_str[0]);
l_end=lp+l_str.length()-1;
s_end=sp+s_str.length()-1;
while((lp<=l_end)&&(sp<=s_end))
{
if(*lp<*sp)
lp++;
else
{
if(*lp=*sp)
{
lp++;
sp++;
}
else
return 0;
}
}
if(sp<=s_end)
return 0;
else
return 1;

}
void main()
{
string l_str("ABCDEFGHLMNOPQRS");
string s_str("DCGSRQPOV");
//string l_str("ABCDE");
//string s_str("BE");
int result = 0;
int l_len = l_str.length();
int s_len = s_str.length();
char * l_st,* l_end,* s_st,* s_end;
l_st=&(l_str[0]);
l_end=&(l_str[0])+l_len-1;

s_st=&(s_str[0]);
s_end=&(s_str[0])+s_len-1;

quick_sort(l_st,l_end);
quick_sort(s_st,s_end);
cout << "long string" << l_str << endl;
cout << "short string" << s_str << endl;

result=compare(l_str,s_str);
cout<<"ok"<< result<<endl;
}


最近程序练得少,写快速排序程序时候花了一番功夫。自己写一篇快排程序之后感觉对快排理解更加深刻了一些。之前对于partition的实现过程不是太理解,现在有了进一步的理解如下:

对于数组[3,1,5,2,7,8],假定取3作为基准元素进行划分,那么划分的目标就是使3的左边都是小于的,3的右边都是大于3的。刚开始3处于0位置,所以左边没有元素,那么就要想办法使得右边的元素都大于3.要使得右边的元素都大于3,那么最右边的肯定需要大于3,于是就对8进行判断,ok,然后就是7,ok,然后是2。2就不符合条件了,所以把3和2对调,得到2,1,5,3,7,8.现在3的右边元素都ok了,但是左边的还无法保证,所以再从最左边的2开始.重复这样的过程,就可以实现partition。partition算法的特点是交替着从左右两侧进行判断。

看到算法1.3的时候,就想到了利用字符的特性来进一步改进算法。假设都是大写字符,那么一共就26个,所以建立长度为26的缓存数组,遍历一次长字符串就可以知道长字符串包含哪些字符。之后对于短字符串中的每个字符,就不需要再去遍历长字符了,直接根据字符减去’A’得到下标就可以知道长字符串中有没有该字符。这种做法是很巧妙的,很好的利用了字符的特性,时间复杂度也优化到了O(m+n),空间复杂度是O(1)。2.1 2.2都是这个思路。

第三种素数的方法就比较新奇了。

博客最后给了一个扩展题。a和b两个字符串,求b串包含a串的最小长度。包含指的就是b的字串包含a中每个字符。

简单想了一下,建立26个字符的缓存数组可以较好地解决问题。当b中包含重复的字符串的时候,则需要进行一些判断方可。准备有空把这个题目的程序也写写。以后遇到字符串比较的算法题,都可以往26个字符缓存数组的思路上去想。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: