问题描述: 给定一个长字符串a和一个短字符串b。请问,如何最快地判断出短字符串b中的所有字符都在长字符串a中?请编写函数bool StringContain(string &a, string &b)实现此功能。为简单起见,假设输入的字符串只包含大写英文字母。
下面举几个例子:
(1)如果字符串a是"ABCD",字符串b是"BAD",答案是true,因为字符串b中的字母都在字符串a中,或者说b是a的真子集。
(2)如果字符串a是"ABCD",字符串b是"BCE",答案是false,因为字符串b中的字母E不在字符串a中。
(3)如果字符串a是"ABCD",字符串b是"AA",答案是true,因为字符串b中的字母A包含在字符串a中。
解题思路:
1.最直接的办法,蛮力轮询。对于这种方法,如果长字符串a的长度为n,短字符串b的长度为m,那么此算法需要比较次数为O(n*m)。显然该方法时间开销很大。
2.排序后轮询,根据题目只包括大写的英文字母,对两个字符串分别进行排序需要操作:O(mlogm)+O(nlogn),之后进行线性扫描需要O(n+m)
参考代码:
#include <bits/stdc++.h>
using namespace std;
bool StringContain( string &a , string &b )
{
sort( a.begin() , a.end() );
sort( b.begin() , b.end() );
int lena = a.length();
int lenb = b.length();
for(int pa = 0 , pb = 0 ; pb < lenb ; )
{
while((pa < lena) && (a[pa] < b[pb]))
{
++pa;
}
if((pa >= lena) || (a[pa] > b[pb]))
{
return false;
}
++pb;
}
return true;
}
int main()
{
string a = "ABCD";
string b[3];
b[0] = "BAD";
b[1] = "BCE";
b[2] = "AA";
for(int i=0;i<3;i++)
{
if(StringContain(a,b[i]))
cout<<a<<" contains "<<b[i]<<endl;
else
cout<<a<<" don't contain "<<b[i]<<endl;
}
return 0;
}
GCC运行结果:
3.位运算法。
将长字符串a的所有字符都存放在一个散列表(hash table)中,然后轮询短字符串b,看b中的每一个字符是否都在散列表中,若果都在则返回true,否则返回false
可以用位运算(26位整数表示)给相应的长字符串a就算出一个相对应的"签名",然后将字符串b的每一个字符都放在a中进行查找。
参考代码:
#include <bits/stdc++.h>
using namespace std;
bool StringContain( string &a , string &b )
{
int hash = 0;
int lena = a.length();
int lenb = b.length();
for( int i = 0 ; i < lena ; i++)
{
hash |= (1 << (a[i] - 'A'));
}
for(int i = 0; i < lenb ; i++)
{
if( ( hash & ( 1 << ( b[i] - 'A' ) ) ) == 0)
return false;
}
return true;
}
int main()
{
string a = "ABCD";
string b[3];
b[0] = "BAD";
b[1] = "BCE";
b[2] = "AA";
for(int i=0;i<3;i++)
{
if(StringContain(a,b[i]))
cout<<a<<" contains "<<b[i]<<endl;
else
cout<<a<<" don't contain "<<b[i]<<endl;
}
return 0;
}
GCC运行结果:
该算法的空间复杂度为O(1),时间复杂度为O(n+m)。