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

题目描述:
假设这有一个各种字母组成的字符串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个字符缓存数组的思路上去想。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值