字符串包含

来源:互联网 发布:格子 玻尔兹曼 软件 编辑:程序博客网 时间:2024/05/17 06:09

问题

*给定两个分别由字母组成的字符串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

将string B中的字符一个一个的与A中进行比较,若全部存在则返回true,反之则返回false,这个方法属于暴力法,肯定可以解出来,但是复杂度会比较高。该种方法的时间复杂度为O(m*n)。

bool StringContains(string &A, string &B){    int i=0;    for(int j=0; j<B.length(); j++) {        for(i=0; (i<A.length() && A[i] != B[j]); i++)            ;        if(i >= A.length())            return false;    }    return true;    }

思路2

上述string都是乱序排列,那么在判断包含前,我们如果将两个string都先进行排序,然后再比较,会不会更好一些呢?例如:

adface--->aacdefdabe--->abde

那么此时在比较的时候我们就没有必要逐个字符进行比较,而只需要比较前面比较小的字符即可,例如b我们只需要比较aac就可以判断出字符”dabe”不包含在”adface”中,这种方法的时间复杂度为两个字串的排序需要(常规情况)O(n^2)次操作,这种排序的复杂度比暴力法还要复杂,所以排序算法是推荐使用快速排序的,其时间复杂度为O(n*logn),之后的线性扫描需要O(m+n)次操作。

void sort(string &a){    char temp;    int end = a.length();    for(int i=0; i<end-1; i++)        for(int j=i+1; j<end; j++){            if(a[i] > a[j]){                temp = a[j];                a[j] = a[i];                a[i] = temp;            }        }}bool StringContains(string &A, string &B){    sort(A);    sort(B);    for(int i=0, j=0; j<B.length();){        while(A[i] < B[j] && i<A.length()){            i++;        }        if(A[i] > B[j] || i>= A.length())            return false;        else            j++;    }    return true;}

思路3

素数相乘法:这种方法属于比较巧妙的类型,将26个字母对应成相应的26个素数,这样就可以将母字符串中的字母使用素数的乘积product来表示,根据素数的特性:素数只能被1和它本身相除,那么就可以使用product来除以子字符串B中的每个字母对应的素数,若能除断,则表明B包含在母数组中,若不能除断,则不在母数组中。该算法的复杂度为O(m+n)。
代码如下:

bool StringContains(string &A, string &B){    const int p[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};    int product = 1;    for(int i=0; i<A.length(); i++){        int x = p[A[i]-'a'];        if(product % x)            product *= x;    }    cout << product << endl;    for(int j=0; j<B.length(); j++){        if(product % (p[B[j] - 'a']) != 0)            return false;    }    return true;}

使用素数相乘法需要注意一下几个问题:

1.溢出问题。当字符串足够长的时候就比较容易溢出。

2.字母大小写问题。在判断这个字符串的时候,其实是隐含了一个条件,那就是判断的字母都是小写,如果判断的字母是大小写混合,那么就更容易溢出,所以这种方法的实际意义不是特别大。

思路4

位运算:代码如下:

bool StringContains(string &A, string &B){    int hash = 0;    for(int i=0; i<A.length(); i++){        hash |=(1 << (A[i]-'A'));    }     for(int j=0; j<B.length(); ++j){        if((hash & (1<<(B[j]-'A'))) == 0)            return false;    }    return true;} 

位运算的思路很简单,那就是假设A~Z有26个字母,那么对应的有26bit位来表示该字符。如该字符存在,则通过移位使得相应的bit位上置为1,这样就可以将string A上的字符都对应到相应的bit位上。那么对于string B上我们该如何操作呢?也是使用移位的方法,然后将string B上每个字符对应的bit位与hash进行&运算,如果结果为0,那就表示string A中没有该字符。如图所示:

hash值表示的是字符串A整体移位过后的值,绿色箭头表示的是单个的字符与其进行&运算过后的值,若&运算结果为1,表明该字符在字符串A中,例如字符A,B。若&运算结果为0,表明该字符不在字符串A中,如C。

0 0