字符串包含类问题

问题描述

给定两个字符串a和b,一长一短,如何快速判断短字符串b中所有元素都在长字符串a中?这里假设字符串中的元素都为大写字母
【例 如】

  1. a = “ABCD”,b = “DBA”,返回true
  2. a = “ABCD”,b = “AAD”,返回true
  3. a = “ABCD”,b = “BCE”,返回false

算法的本质在于用更小的时间消耗获得同样的效果,所以,除了暴力轮询这种方法之外,我们应该努力寻找其他更快速有效的算法来解决这类问题。

方法一:排序+轮询查找

排序往往会使查找变得更加快捷,而众多排序算法中,快速排序在处理大数据集是很有优势的。当然对于本题,数据集可大可小,因此,如果要在排序算法上下功夫的话,可以采用上一文讲的C语言快速排序算法及三种优化方式 中3.4节的思想。
这里小编偷个小懒,采用一般快速排序(即不考虑 三数取中 和 小数据的情况,采用挖坑填坑的快排,具体参看上一文讲的C语言快速排序算法及三种优化方式

step 1: 将a和b使用快排成为递增序列,复杂度o(nlogn)
step 2: b中元素与a匹配,匹配成功则返回false,复杂度O(m+n)

注意:不做排序的直接匹配,由于序列不是有序,因此每一次匹配都要把a数组遍历一次,复杂度O(n*m)

排序+查找代码实现

int first_ele_fast_sort( char array[] , int low , int high )//注意是针对字符串的快排  char
{
    char target = array[low];//偷个懒,直接用数组中的第一个元素作为中轴元素
    while( low < high )
    {
        while( low < high && array[high] >= target  )
            high--;
        array[low] = array[high];//此时high位置上的元素为待处理元素

        while( low < high && array[low] <= target   )
            low++;
        array[high] = array[low];//将大于target值的元素放到待处理的high位置上,那么此时,low位置变为待处理
    }
    array[low] = target; //以上,省去交换,变为“挖坑赋值”,降低时间复杂度
    return low;
}
void fast_sort( char array[] , int low , int high )
{
    if( low < high )
    {
        int index;
        index = first_ele_fast_sort(array,low,high);
        fast_sort(array,low,index-1);
        fast_sort(array,index+1,high);
    }
}
bool string_contain( char a[] , int a_len , char b[] , int b_len )//a b均为字符串
{
    fast_sort(a,0,a_len-1);//字符串a快排
    fast_sort(b,0,b_len-1);//字符串b快排

    int a_index,b_index;
    for( b_index = 0 , a_index = 0 ; b_index < b_len ; b_index++ )//b在a中找伙伴,所以b为外循环
    {
        while( a_index < a_len && a[a_index] < b[b_index] )//a,b均为增序
            a_index++;
        if( a_index >= a_len || a[a_index] > b[b_index] )
            return false;//b中任意一个元素轮询a后没找到匹配,则直接return false  无需再找下去了--降低复杂度
    }
    return true;
}

方法二:构建质数数组,取余判断

step 1:构建大小为26的质数数组,将26个字母分别用{1,2,3,5,7,11…}这些质数元素代表;
step 2:将a数组里对应的所有质数相乘,得到一个积;
step 3:用积对b数组中每个元素对应的质数取余,如果余数为0,说明此元素在a数组中,反之则不在

本算法有个缺陷,因为质数相乘的结果会很大,从而导致整数溢出(前16个字母对应的质数相乘会超出long long类型所能刚表示的最大整数边界),所以这种方法不可取。

bool StringContain(char* str1, char* str2)
{
    int mapping[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};

    //遍历一遍str1表,转成素数乘积
    int i;
    int x=0;
    long long res1 = 1;
    for(i=0; i<strlen(str1); i++)
        res1 = res1*mapping[str1[i]-'A'];
    //遍历一遍str2表,用res1与每一个元素做除法,一旦有余数则说明不包含
    for(i=0; i<strlen(str2); i++)
    {
        x = mapping[str2[i]-'A'];
        if(res1%x!=0)
            return false;
    }
    return true;
}

方法三:构建哈希表

step 1: 将a中元素放入哈希表
step 2: b中元素通过哈希地址访问哈希表,若找到相同元素,则false

本方法的时间复杂度为:O(1)

构建哈希表方法代码实现

1、传统方法
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAXSIZE 100
typedef struct hash_node//链表法避免散列表冲突
{
    int element;
    struct hash_node *next;
}hash_node,hash_link[MAXSIZE];

hash_link L;//全局变量,方便后几个函数承接

int hash_fun( int at_frist )
{
    return at_frist%MAXSIZE;
}//取余法构建哈希函数
void char2hashList( char array[] , int array_len )
{
    int i;
    //哈希表初始化
    for( i = 0 ; i < MAXSIZE ; i++ )
    {
        L[i].element = i;
        L[i].next = NULL;
    }
    //字符串插进哈希表中
    for( i = 0 ; i < array_len ; i++ )
    {
        hash_node *p;
        p = (hash_node*)malloc(sizeof(hash_node));

        p->element = array[i] - 'A';
        int hash_index = hash_fun(array[i]-'A');

        //头插法插入hash_index所在链表
        p->next = L[hash_index].next;
        L[hash_index].next = p;
    }//以上MAXSIZE只要足够大于array_len,基本保证每个hash_index的链表就一个元素,大大降低了后面的查找时间
}//函数完成将a数组元素放入哈希表中

bool string_contain( char a[] , int len_a , char b[] , int len_b )
{
    int i;
    //先将a纳入哈希表
    char2hashList(a,len_a);


    for( i = 0 ; i < len_b ; i++ )
    {
        int b_hash_index = hash_fun(b[i]-'A');//将数组b的元素进行哈希地址转换

        hash_node *p;
        p = L[b_hash_index].next;//p指向b经哈希地址转换后的结点

        while(p)
        {
            if(p->element != b[i]-'A')//在b_hash_index下标所在的链表中,找与b[i]-'A'匹配的元素
                p = p->next;
            else
                break;
        }
        if( !p )
            return false;
    }
    return true;
}
简单哈希

根据题目特点,构建仅含26个字母的特殊哈希表,哈希函数映射规则为:index = str[i]-‘A’ hash[index]=true/false

bool StringContain(char* str1, char* str2)
{
    /**将str1的元素按照哈希函数的规则存入哈希表**/
    bool hash[26]={false};//0-false表示没有,1-true表示有
    int i=0;
    for(i=0; i<strlen(str1); i++)
    {
        int index = str1[i]-'A';
        if(hash[index] == false)
            hash[index] = true;
    }//这样一来,array数组就记录了仅由字母组成的字符串str1中,所包含的字母情况

    //接下来遍历str2字符串,一旦通过相同的哈希规则没在哈希表array中找到所映射的字母,则直接返回false
    for(i=0; i<strlen(str2); i++)
    {
        int index = str2[i]-'A';
        if(hash[index]==false)
            return false;
    }
    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值