02字符串包含及相关问题

题目描述:

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

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

String 1: ABCDEFGHLMNOPQRS

String 2: DCGSRQPO

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

 

        如果是下面两个字符串:  

String 1:ABCDEFGHLMNOPQRS  

String 2: DCGSRQPZ 

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

 

1O(n*m)的轮询方法

        判断一个字符串是否在另一个字符串中,最直观也是最简单的思路是,针对第二个字符串string2中每一个字符,一一与第一个字符串string1中每个字符依次轮询比较,看它是否在第一个字符串string1中。代码如下:

int  CompareString(string  LongString,  string  ShortString)   

{   

    int  i,j; 

    for (i=0;  i<ShortString.length();  i++)   

    {   

        for (j=0;  j<LongString.length();  j++) //O(n*m)   

        {   

            if (LongString[j]  ==  ShortString[i]) 

            {   

                  break;   

            }   

        }   

        if (j == LongString.length())   

        {   

            cout << "false"<< endl;   

            return 0;   

        }   

    }   

    cout << "true" <<endl;   

    return 1;   

}

 

2O(mlogm)+O(nlogn)+O(m+n)的排序方法

        一个稍微好一点的方案是先对这两个字符串的字母进行排序,然后同时对两个字串依次轮询。两个字串的排序需要(采用最常用的快速排序)O(m log m) + O(n log n)次操作,之后的线性扫描需要O(m+n)次操作代码如下:

void  compare(string  str1, string  str2)                         //str1 is longstring, str2 is shortstring

    int posOne = 0; 

    int posTwo = 0; 

    while (posTwo  <  str2.length()  &&  posOne  < str1.length()) 

    { 

        while (str1[posOne] < str2[posTwo]&& posOne < str1.length() - 1) 

            posOne++; 

        //如果和str2相等,那就不能动。只有比str2小,才能动。 

         

        if (str1[posOne] != str2[posTwo]) 

            break; 

         

        //posOne++;    

        // str1Pos是否自增,取决于str2中是否有重复字符串

 

        posTwo++; 

    } 

                 

    if (posTwo == str2.length()) 

        cout << "true" <<endl; 

    else 

        cout << "false"<< endl; 

 

3O(n+m)的计数排序方法

       同样是先排序,后比较的算法,因为需要排序的元素为26个字母,所以,可以采用线性时间的排序算法,比如计数排序,这样,需要的时间为:O(n) + O(m) +O(n+m) = O(n+m)。代码不再赘述。

 

4:O(n+m)的hashtable的方法

        可以对短字串进行轮询(最好是应该把短的先存储,那样,会降低题目的时间复杂度),把其中的每个字母都放入一个Hashtable里。然后轮询长字符串,具体算法如下:

 a、hash[26],先全部清零,然后扫描短的字符串,若有相应的置1,

 b、计算hash[26]中1的个数,记为m

 c、扫描长字符串的每个字符a;若原来hash[a] == 1 ,则修改hash[a]= 0,并将m减1;若hash[a] == 0,则不做处理

 d、若m == 0 or 扫描结束,退出循环。

 

代码如下:

    int  hash[26]  =  {0}; 

    // num为辅助数组中元素个数 

    int  num  =  0; 

 

    // 扫描短字符串 

    for (int  j  =  0;  j  <  str2.length(); j++) 

    { 

        // 将字符转换成对应辅助数组中的索引 

        int  index  = str1[j] - 'A'; 

 

        // 如果辅助数组中该索引对应元素为0,则置1,且num++;  这样也适用于str2 中有重复字母的情况。

        if (hash[index]  ==  0) 

        { 

            hash[index]  =  1; 

            num++; 

        } 

    } 

 

    // 扫描长字符串 

    for (int  k = 0;  k < str1.length();  k++) 

    { 

        int index = str1[k] - 'A'; 

 

        // 如果辅助数组中该索引对应元素为1,则num--;为零的话,不作处理(不写语句)。 

        if(hash[index]  ==  1) 

        { 

            hash[index]  =  0; 

            num--; 

            if(num  ==  0)    //m==0,即退出循环。 

                break; 

        } 

    } 

 

    // num0说明长字符串包含短字符串内所有字符 

    if (num == 0) 

        cout << "true" <<endl; 

    else 

        cout << "false"<< endl; 

 

5: O(n+m)的素数方法

        给每个字母分配一个素数,从2开始,往后类推。这样A将会是2,B将会是3,C将会是5,等等。具体思路如下:

a.定义最小的26个素数分别与字符'A'到'Z'对应。

b.遍历长字符串,求得每个字符对应素数的乘积。

c.遍历短字符串,判断乘积能否被短字符串中的字符对应的素数整除。

d.输出结果。

        上述算法的时间复杂度为O(m+n),空间复杂度为O(1)。该算法提供的“是一种更、更、更有趣的方案。”,代码如下:

 

int  primeNumber[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  main() 

    string  strOne = "ABCDEFGHLMNOPQRS"; 

    string  strTwo = "DCGSRQPOM"; 

 

    // 这里需要用到大整数 

    CBigInt  product  =  1;   //大整数除法的代码,下头给出。 

 

    // 遍历长字符串,得到每个字符对应素数的乘积 

    for (int i = 0;  i < strOne.length();  i++) 

    { 

        int  index = strOne[i] - 'A'; 

        product  =  product * primeNumber[index]; 

    } 

 

    // 遍历短字符串 

    for (int  j = 0;  j< strTwo.length();  j++) 

    { 

        int  index = strTwo[j] - 'A'; 

        // 如果余数不为0,说明不包括短字串中的字符,跳出循环 

        if (product % primeNumber[index] != 0) 

            break; 

    } 

 

    // 如果积能整除短字符串中所有字符则输出"true",否则输出"false"。 

    if (strTwo.length() == j) 

        cout  << "true" << endl; 

    else 

        cout  << "false" << endl; 

    return  0; 

 

6Bit-map方法

        所谓的Bit-map就是用一个bit位来标记某个元素对应的Value,而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。

        先举几个Bit-map的用例:

        a:排序:

#define  BYTESIZE  8  

//将位数组p的第posi位置为1

void  SetBit(char *p,  int  posi) 

{     

    for(int  i=0;  i< (posi/BYTESIZE);  i++)      

    {        

        p++;     

    }       

    *p =*p|(0x01<<(posi%BYTESIZE));       //将该Bit位赋值1     

    return; 

}   

 

void  BitMapSortDemo()  

{    

    //为了简单起见,我们不考虑负数    

    int  num[] = {3,5,2,10,6,12,8,14,9};   

    //BufferLen这个值是根据待排序的数据中最大值确定的   

    //待排序中的最大值是14,因此只需要2个Bytes(16个Bit)     

    //就可以了。     

    const  int  BufferLen= 2;     

    char  *pBuffer  =  new  char[BufferLen];  

     

    //要将所有的Bit位置为0,否则结果不可预知。     

    memset(pBuffer,  0,  BufferLen);     

     

    for(int  i=0;  i<9;  i++)     

    {         

        //首先将相应Bit位上置为1          

        SetBit(pBuffer,  num[i]);    

    }       

    //输出排序结果      

    for(i=0;  i<BufferLen;  i++)  //每次处理一个字节(Byte)     

    {          

        for(int  j=0;  j<BYTESIZE;  j++)  //处理该字节中的每个Bit位      

        {                      

            if((*pBuffer & (0x01<<j)) ==  (0x01<<j))         

            {               

                printf("%d ", i*BYTESIZE+ j);       

            }   

        }   

        pBuffer++;    

    } 

    printf("\n"); 

}    


        b:已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。 

        8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。

 

        c:2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

        将 bit-map扩展一下,用2bit表示一个数即可,0表示未出现,1表示出现一次,2表示出现2次及以上,在遍历这些数的时候,如果对应位置的值是0, 则将其置为1;如果是1,将其置为2;如果是2,则保持不变。

 

        利用bit-map解决字符串是否包含问题的思路是:每个字母的ASCII码值,可以对应一个位图中的位。先遍历第一个长字符串,生成一个“位图字典”。 然后,遍历第二个短字符串,用查字典的方式较检即可。算法时间复杂度为O(m+n),空间复杂度为O(1)。代码如下:

#define  getbit(x)  (1<<(x-'a')) 

void  a_has_b(char * a,  char * b) 

    int  i = 0; 

    int  dictionary = 0; 

    int  alen = strlen(a); 

    int  blen = strlen(b); 

     

    for(i=0;  i<alen;  i++) 

        dictionary |= getbit(a[i]); 

 

    for(i=0; i<blen;  i++) 

    { 

                if(dictionary  !=  (dictionary|getbit(b[i]))) 

                            break; 

    } 

     

    if(i == blen) 

        printf("YES! A hasB!/n"); 

    else 

        printf("NO!  Char at %d is not found indictionary!/n",i); 

         该算法与HASHTABLE算法的本质相同,只是形式不一样而已。

 

7字符串匹配问题

        题目描述:

        假设两个字符串中所含有的字符和个数都相同我们就叫这两个字符串匹配,比如:abcda和adabc,由于出现的字符个数都是相同,只是顺序不同,所以这两个字符串是匹配的。

        要求高效实现下面的函数: boolenIs_Match(char *str1,char *str2)。

        分析:可以看出,此字符串的匹配问题,是与上述字符串包含的问题相类似的,这个问题可以先排序再比较,也可以利用hash表进行判断。这里给出一种hash表的方法,原理已在上文中阐明了,代码如下:

bool  Is_Match(const  char  *strOne, const  char  *strTwo) 

    int  lenOfOne= strlen(strOne); 

    int  lenOfTwo = strlen(strTwo); 

 

    // 如果长度不相等则返回false 

    if (lenOfOne  !=  lenOfTwo) 

        return  false; 

 

    // 开辟一个辅助数组并清零 

    int  hash[26]= {0}; 

     

    // 扫描字符串 

    for (int  i = 0;  i < strlen(strOne);  i++) 

    { 

        // 将字符转换成对应辅助数组中的索引 

        int  index = strOne[i] - 'A'; 

         

        // 辅助数组中该索引对应元素加1,表示该字符的个数 

        hash[index]++; 

    } 

 

    // 扫描字符串 

    for (int  j = 0;  j < strlen(strTwo);  j++) 

    { 

        int  index = strTwo[j] - 'A'; 

          

        // 如果辅助数组中该索引对应元素不为0则减1,否则返回false 

        if (hash[index]!= 0) 

           hash[index]--; 

        else 

            return  false; 

    } 

    return  true; 

 

http://blog.csdn.net/v_JULY_v/article/details/6347454

 

转载于:https://www.cnblogs.com/gqtcgq/p/7247194.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值