挑战面试编程:字符串包含
字符串查找是在一个大的文本中查找一个给定的模式串,常用的算法有KMP、BM、Sunday等。而字符串包含要求比较低:只要字符包含就行,不需要模式串作为整体出现。
例如,S="abcd",T="ac",做查找运算,则T不在S中;做包含运算,T包含在S中。
方法一
最简单的就是对T中的每一个字符都在S中进行查找,都存在,则包含。
/*
字符串包含--朴素法
判断字符串T中的字符是否全都在字符串S中
*/
bool contains(char *S, char *T)
{
assert(S && T);
int SLen, TLen;
SLen = strlen(S), TLen = strlen(T);
int i, j;
i = 0, j = 0;
while (j < TLen)
{
i = 0;
while (i < SLen)
{
if (T[j] == S[i])
break;
i++;
}
if (i == SLen)
return false;
j++;
}
return true;
}
在最坏的情况下,对于T中的每一个字符都遍历一遍S,则时间复杂度是O(mn)
方法二
先对对两个字符串排序,排序后再检索,这样的检索效率高。
/*
字符串包含--排序比较法
判断字符串T中的字符是否全都在字符串S中
*/
bool contains2(char *S, char *T)
{
assert(S && T);
int SLen, TLen;
SLen = strlen(S), TLen = strlen(T);
/*
先排序
sort(first, last)函数对[first, last)范围的数据按升序排序
*/
sort(S, S + SLen);
sort(T, T + TLen);
//puts(S);
//puts(T);
int i, j;
i = 0, j = 0;
while (j < TLen)
{
while (i < SLen && S[i] < T[j])
i++;
if (i == SLen || S[i] > T[j])
return false;
j++;
}
return true;
}
S的长度是n,T的长度是m,则排序是O(nlogn+mlogm),检索是O(m+n),时间复杂度是O(nlogn+mlogm+m+n)。排序可采用更高效的计数排序。
方法三
素数法,利用素数特有的性质:除1和本身外再无其它因数。
/*
字符串包含--素数法
判断字符串T中的字符是否全都在字符串S中
*/
bool contains3(char *S, char *T)
{
assert(S && T);
int SLen, TLen;
SLen = strlen(S), TLen = strlen(T);
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, 91, 97 };
long long mul = 1;
int i, x;
for (i = 0; i < SLen; i++)
{
x = prime[S[i] - 'a'];
//为了防止mul过大,去掉重复的素数
if (mul % x)
mul *= x;
}
for (i = 0; i < TLen; i++)
if (mul % prime[T[i] - 'a'])
return false;
return true;
}
最好O(n),在遍历T的第一个字符时就失败。最坏O(n+m),需要遍历完T。素数法并不完美,素数相乘的乘积可能过大,造成溢出。故效率高,但不推荐使用。
方法四
对两字符串构造位图,然后进行集合的包含运算判断。
/*
字符串包含--位图法
判断字符串T中的字符是否全都在字符串S中
*/
bool contains4(char *S, char *T)
{
assert(S && T);
bitset<26> s, t;
int SLen, TLen;
SLen = strlen(S), TLen = strlen(T);
s.reset();
t.reset();
int i;
for (i = 0; i < SLen; i++)
s.set(S[i] - 'a');
for (i = 0; i < TLen; i++)
t.set(T[i] - 'a');
//若S包含T,则集合s包含t
//或者return (s.to_ulong() & t.to_ulong()) == t.to_ulong();
return (s.to_ulong() | t.to_ulong()) == s.to_ulong();
}
时间复杂度显然是O(m+n)。
方法五
这是位运算法,也叫哈希法,它与位图法本质上是一样的。
/*
字符串包含--位运算法
判断字符串T中的字符是否全都在字符串S中
*/
bool contains5(char *S, char *T)
{
assert(S && T);
int SLen, TLen;
SLen = strlen(S), TLen = strlen(T);
int i, bit;
bit = 0;
for (i = 0; i < SLen; i++)
bit |= (1 << (S[i] - 'a'));
for (i = 0; i < TLen; i++)
if ((bit & (1 << (T[i] - 'a'))) == 0)
return false;
return true;
}
所有内容的目录