故特别声明:所有问题的解决思路都是网上大牛们提供的,感谢他们的分享。我做的事情是理解这些思路,用自己的话转述一遍,而不是简单地复制粘贴。我也会注明资料的来源,如果作者觉得有侵权行为,请联系我。
这个问题是看了http://blog.youkuaiyun.com/v_JULY_v/archive/2011/04/23/6347454.aspx, https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/01.02.md(感谢@v_JULY_v)。
问题描述:有两个任意字符串sting A, sting B, 实现如下函数 int Contain( string &a, sting &b),如果a 里面包含b所有出现的字符,函数返回TRUE,其它情况返回FALSE。相当于集合上的包含问题。
@v_JULY_v提供了大致四种思路:轮训,排序轮训,素数相乘,及利用hash查找的思想(包括)。
学习了解了这些思路之后,我比较喜欢的是轮训这种方法,这种方法很笨,但却是最直观,最容易想到的方法。但除此之外,应该掌握一种复杂度比较小的一种方法。采用hash查找的方法,可以将时间复杂度降到O(m+n),空间复杂度降到O(1)。
采用hash查找思想的思路这样的:
step1:先分配一个整数数组hash[max],数组大小为字符集个数(ASCII码为256),并初始化为0。
step2:然后开始遍历Stirng A,然后将整形数组中位置为这些字符的ASCII值的元素增1;比如出现‘A’,的ascii为65,那么hash[65]++;
step3:遍历StingB,如果数组里面那些位置为SringB字符集里的ASCII值的元素都不为0,则字符串A包含字符串B,
代码实现:
int contain(const char *stra, const char *strb)
{
int hash[256] = {0};
const char *p = stra;
while( *p != '\0')
{
hash[*p++]++;
}
p = strb;
while( *p != '\0')
{
if(hash[*p] == 0)
return -1;// failed!
p++;
}
return 0;// succeed!
}
这个思路可以改进为:用比特位来表示StringA里字符的出现。具体代码参见@v_JULY_v相应的代码。不过他把问题进行了简化,字符集仅限于大写字母,故只需要一个整数,就可以表示所有的字符出现标志,其实只要26个。但是字符集扩大的话,比如128,256,这就无法用一个int或者long来表示了。可以用结构体来实现。
typedef struct
{
long long a;
long long b;
long long c;
long long d;
} hash;
int move(hash *h,char a)//
{
long long moveval = a % 64;
if( a >=0 && a < 64)
h->a |= 1 << moveval;
else if(a > 63 && a < 128)
h->b |= 1 << moveval;
else if( a > 127 && a < 192)
h->c |= 1 << moveval;
else //if(a > 191 && a < 256)
h->d |= 1 << moveval;
return 0;
}
int hashflag(hash *h, char c)
{
int moveval = c % 64;
if(c >=0 && c < 64)
return h->a & (1 << moveval);
else if(c > 63 && c < 128)
return h->b & ( 1 << moveval);
else if(c > 127 && c < 192)
return h->c & ( 1 << moveval);
else
return h->d & ( 1 << moveval);
}
int strContain(const char *stra, const char *strb)
{
hash h;
h.a = 0;
h.b = 0;
h.c = 0;
h.d = 0;
const char *p = stra;
while(*p != '\0')
move(&h, *p++);
p =strb;
while(*p != '\0')
{
if(hashflag(&h, *p++) == 0)
return -1;
}
return 0;
}