数据结构学习笔记2-字符串包含(快速排序和哈希表)

本文参考了文章《字符串包含》

题目描述

给定两个分别由字母组成的字符串A和字符串B,字符串B的长度比字符串A短。请问,如何最快地判断字符串B中所有字母是否都在字符串A里?

为了简单起见,我们规定输入的字符串只包含大写英文字母,请实现函数bool StringContains(string &A, string &B)

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

String 1:ABCD

String 2:BAD

答案是true,即String2里的字母在String1里也都有,或者说String2是String1的真子集。

如果是下面两个字符串:

String 1:ABCD

String 2:BCE

答案是false,因为字符串String2里的E字母不在字符串String1里。

同时,如果string1:ABCD,string 2:AA,同样返回true。

这里只介绍两种较好地方法:
解法1:
如果允许排序的话,我们可以考虑下排序。比如可先对这两个字符串的字母进行排序,然后再同时对两个字串依次轮询。两个字串的排序需要(常规情况)O(m log m) + O(n log n)次操作,之后的线性扫描需要O(m+n)次操作。

关于排序方法,可采用最常用的快速排序,参考代码如下(在mac下成功运行):

void swap(char *s,long i,long j)//字符串的里面的单个字符交换函数
{
    char temp=*(s+i);//使用指针操作
    *(s+i)=*(s+j);
    *(s+j)=temp;
}

 /**
 *  将传入的字符数组s根据选取的主元来分成三部分,参考《算法导论》96页
 *
 *  @param s  传入的字符数组指针
 *  @param lo 需要处理的字符数组首下标
 *  @param hi 需要处理的字符数组的末下标
 *
 *  @return 主元x最后所在的下标
 */
unsigned long parttion(char *s,long lo,long hi)//hi传入的是最后一个字符串(不是'\0')对应的下标的数字
{
    char key;
    key=*(s+hi);//选取*(s+hi)为主元
    long i=lo-1;
    for (long j=lo; j<hi; j++) {
        if (s[j]<=key) {
            i++;
            swap(s,i,j);

        }
    }

    swap(s,i+1,hi);//最后还要将主元交换一次
    return i+1;
}

void quickSort(char *s,long lo,long hi)
{
    if (lo<hi) {
        long k=parttion(s,lo,hi);
        quickSort(s,lo,k-1);
        quickSort(s,k+1,hi);
    }
}

bool compareString(char *longs,char *shorts)
{
    int i=0;
    for (; i<strlen(shorts); i++)
    {
        if (i>0)
        {
            if (*(shorts+i)==*(shorts+i-1))
            {
                continue;
            }
        }
        for (int j=0; j<strlen(longs); j++)
        {
            if (j>0)
            {
                if (*(longs+j)==*(longs+j-1))
                {
                    continue;
                }
            }
            if (*(longs+j)!=*(shorts+i))
            {
                if (j==strlen(longs)-1)
                {
                    return false;
                }
                continue;
            }
            break;
        }

    }
    return true;
}

int main(int argc, const char * argv[])
{
    char mainstatus;
    char s1[]="28713564";
    char s2[]="22879";
    quickSort(s1, 0, strlen(s1)-1);
    quickSort(s2, 0, strlen(s2)-1);

    mainstatus = compareString(s1,s2);

    if (mainstatus)
    {
        printf("true\n");
    } else {
        printf("false\n");
    }

    return 0;
}

解法2:

上述方案中,较好的方法是先对字符串进行排序,然后再线性扫描,总的时间复杂度已经优化到了:O(m+n),貌似到了极限,还有没有更好的办法列?

我们可以对短字串进行轮询(此思路的叙述可能与网上的一些叙述有出入,因为我们最好是应该把短的先存储,那样,会降低题目的时间复杂度),把其中的每个字母都放入一个Hashtable里(我们始终设m为短字符串的长度,那么此项操作成本是O(m)或8次操作)。然后轮询长字符串,在Hashtable里查询短字符串的每个字符,看能否找到。如果找不到,说明没有匹配成功,轮询长字符串将消耗掉16次操作,这样两项操作加起来一共只有8+16=24次。
当然,理想情况是如果长字串的前缀就为短字串,只需消耗8次操作,这样总共只需8+8=16次。

参考代码如下:

#include<stdio.h>
#include<string.h>
int main()
{
    char *str1="abcdefghijklmnopqrs";
    char *str2="cdgft";

    int hash[26]={0};//自己定义的哈希表,因为有26个字母,所以是26!

    int num=0;//计算哈希表中1的个数

    for(int j=0;j < strlen(str2);j++)
        {
            int index=str2[j]-'a';

            if(hash[index]==0)
                {
                    hash[index]=1;
                    num++;
                }
        }

    for(int k=0;k<strlen(str1);k++)
        {
            int index=str1[k]-'a';

            if(hash[index]==1)
                {
                    hash[index]=0;
                    num--;
                    if(num==0)
                        break;
                }
        }


    if(num==0)
        printf("true\n");
    else
        printf("false\n");
    return 0;
}

本文参考网址:
《程序员编程艺术:第二章、字符串是否包含问题》
《十一、从头到尾彻底解析Hash 表算法》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值