编程之法1.2字符串的包含

题目要求:

  给定一长字符串a和一短字符串b。请问,如何最快地判断出短字符串b中的所有字符是否都在长字符串a中?请编写函数实现此功能。
  为简单起见,假设输入的字符串只包含大写英文字母。下面举几个例子。
  1.如果字符串a是"ABCD",字符串b是"BAD",答案是true,因为字符串b中的字母都在字符串a中,或者说b是a的真子集。
  2.如果字符串a是"ABCD",字符串b是"BCE",答案是false,因为字符串b中的字母E不在字符串a中。
  3.如果字符串a是"ABCD",字符串b是"AA",答案是true,因为字符串b中的字母都在字符串a中。
  为了以下说明方便,我们将a字符串长度设为n,b字符串长度设为m。

解法一:暴力查询

  将b的每个字符串都到a中去查找一遍,查看是否存在。
  时间复杂度O(nm),空间复杂度O(1)。

bool StringContain_way1(string &a,string &b)
{
    for(int i=0;i<b.length();i++)
    {
        bool exist=false;
        for(int j=0;j<a.length();j++)
        {
            if(a[j]==b[i])
            {
                exist=true;
                break;
            }
        }
        if(!exist)
        {
            return false;
        }
    }
    return true;
}

解法二:排序后查询

  先将原始字符串排序,再把b的字符串到a中去找,因为排过序,所以遍历a只要顺次遍历下去就行了。
  c++sort用的是快速排序,时间复杂度是O(mlogm+nlogn),后面遍历时间复杂度是O(m+n),总时间复杂度是O(m+n+mlogm+nlogn),当n>>m时,总时间复杂度是(nlogn)。
  空间复杂度O(1)。

bool StringContain_way2(string &a,string &b)
{
    sort(a.begin(),a.end());
    sort(b.begin(),b.end());
    int i=0,j=0;
    while(i<a.length() && j<b.length())
    {
        if(a[i]==b[j])
            j++;
        else if(a[i]<b[j])
            i++;
        else
            return false;
        if(i==a.length())
            return false;
    }
    return true;

}

解法三:素数相乘

  对于a,我们让每个字母都和一个素数对应,比如A对应2,B对应3,C对应5,以此类推。然后将a中对应的素数相乘得到一个乘积,称为a的素数积。
  对于b,我们也使用相应的对应规则,每转化一个字母,就将a的素数积除以转化对应的素数,如果除不尽说明a没有这个字母,则退出;如果除得尽,说明a有这个字母,那接着转化b中的字母,直到转化完或者中途退出。
  此方法时间复杂度O(m+n),当然最好的时候为O(n),就是b的第一个字母就不在a中。空间复杂度O(1)。

bool StringContain_way3(string &a,string &b)
{
    const int prime[26]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101};
    long long multi_a=1;
    for(int i=0;i<a.length();i++)
    {
        int x=prime[a[i]-'A'];
        if(multi_a%x)
            multi_a*=x;
    }
    for(int i=0;i<b.length();i++)
    {
        int x=prime[b[i]-'A'];
        if(multi_a%x)
            return false;
    }
    return true;
}

  这种方法看似可行,然而比较麻烦,因为前16个素数相乘的结果就已经超过long long能表示的范围了,所以如果想要用,结果得用字符串来存,当然得自己写一个字符串乘除法。

解法四:计数统计法

  利用计数排序法的原理,因为字符串里只有A-Z这26个字母,我们可以定义两个26位的数组,然后遍历两个字符串,如果某个字母出现,就把这一位设置成1。判断错误条件是b的这一位为1,a的这一位为0。
  此方法的时间复杂度为O(m+n),空间复杂度为O(1)。

bool StringContain_way4(string &a,string &b)
{
    int p[26]={0};//记录a的字母
    int q[26]={0};//记录b的字母
    for(int i=0;i<a.length();i++)
        p[a[i]-'A']=1;
    for(int i=0;i<b.length();i++)
        q[b[i]-'A']=1;
    for(int i=0;i<26;i++)
    {
        if(q[i]==1 && p[i]==0)
            return false;
    }
    return true;
}

解法五:哈希散列法

  把a的所有字母存入哈希表中,然后遍历字符串b,看b中字母是否都在哈希表中。
  此方法的时间复杂度为O(m+n),空间复杂度为O(n)。
  因为C++使用哈希表的STL很麻烦,我这里就先用python写了。
  Python中的dict的底层是依靠哈希表(Hash Table)进行实现的,使用开放地址法解决冲突。所以其查找的时间复杂度会是O(1)。

def StringContain_way5(a,b):
    dict={}
    for i in a:
        dict[i]=1
    for j in b:
        if(not(j in dict)):
            return False
    return True

解法六:位运算法

  其实在解法四的时候就能感觉到,申请一个int数组是多余的,应该申请bool数组。再进一步考虑,其实我们不需要数组,也不需要一整个哈希表,我们只需要一个数,取其中26位的0、1值来表示字母是否出现就行了。表示好之后在遍历b的时候只需要看对应位是否为1就行了。
  此方法的时间复杂度为O(m+n),空间复杂度为O(1)。

bool StringContain_way6(string &a,string &b)
{
    int flag=0;
    for(int i=0;i<a.length();i++)
    {
        flag |=(1<<(a[i]-'A'));
    }
    for(int i=0;i<b.length();i++)
    {
        if((flag&(1<<(b[i]-'A')))==0)
            return false;
    }
    return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值