首页 > 代码库 > 字符串包含
字符串包含
字符串包含
题目:假设这有一个各种字母组成的字符串A,和另外一个字符串B,字符串里B的字母数相对少一些。什么方法能最快的查出所有小字符串B 里的字母在大字符串A里都有?
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
a是长字符串,b是短字符串
方法一:双循环比较法(轮询法)
针对b中的每一个字符,逐个与a中每一个字符比较
伪代码:
bool StringContain(string &a, string &b)
{
for(int i = 0; i < b.length(); i++)
{
for(int j = 0; (j < a.length()) && (a[j] != b[i]); j++);
if(j >= a.length())
{
return false;
}
}
return true;
}
复杂度:O(n*m)时间开销过大
方法二、排序+轮询法
先对两个字符串的字母进行排序,之后同时对两个字符串依次线性扫描轮询
伪代码:
//注意AB中可能包含重复字符,所以注意A下标不要轻易移动,这种方法改变了字符串,如果不想改变可以选择复制bool StringContain(string &a, string &b)
{
sort(a.begin(), a.end());
sort(b.begin(), b.end());
for(int pa = 0, pb = 0; pb < b.length();)
{
while((pa < a.length()) && (a[pa] < b[pb]))
++pa;
if((pa >= a.length()) || (a[pa] > b[pb]))
return false;
++pb;
}
return true;
}
注意:这里解决了一个如果存在相同字符问题,一定要注意不能直接i++,j++,字符类题目一定要注意考虑字符相同的情况,该算法的复杂度为O(mlogm) + O(nlogn) + O(m+n)
方法三:数组存储法
首先标记短字符串中有哪些字符,则在store数组中标记为true,store数组其实就是起到一个映射的作用;之后遍历长字符串,如果为true的则辩称false,否则不处理;最后遍历store数组,如果所有元素都是false那么说明短字符串中元素长字符串中全部有,否则没有。
伪代码:
bool store[maxn];
memset(store, false, 58);
for(i = 0; i < len1; i++)
{
store[short[i] - 65] = true;
}
for(i = 0; i < len2; i++)
{
if(store[long[i] - 65] != false)
store[long[i] - 65] = false;
}
for(i = 0; i < maxn; i++)
{
if(store[i] != false)
{
短字符中有长字符中没有的字符;
break;
}
if(i == len2-1)//其实也可以通过return来完成
{
短字符中没有长字符中没有的字符;
}
}
复杂度为O(m+n)
方法四:O(n)到O(m+n)的素数方法
思路:
① 定义最小的26 个素数分别与字符‘A‘到‘Z‘对应。
② 遍历长字符串,求得每个字符对应素数的乘积。
③ 遍历短字符串,判断乘积能否被短字符串中的字符对应的素数整除。
伪代码:
//这个方法只有理论意义,因此整数乘积很大,有溢出风险bool StringContain(string &a, string &b)
{
const 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,91,97,101};
int product = 1;
//遍历长字符串
for(int i = 0; i < a.length(); i++)
{
int index = a[i] - ‘A‘;
product *= primeNumber[index];
}
//遍历短字符串
for(int j = 0; j < b.length(); j++)
{
int index = b[j] - ‘A‘;
if(product % primeNumber[index] != 0)
return false;
}
return true;
}
纸质手写代码易犯错误:
① 91不是素数
② product初始化为1,不是0
待改进地方
这个程序虽然很巧妙,运用数学知识降低了时间复杂度
1.只考虑大写字符,如果考虑小写字符和数组的话,素数数组需要更多素数
2.没有考虑重复的字符,可以加入判断重复字符的辅助数组。
方法五、哈希表
思路1:将短的字符串存储在哈希表hashTable中,对于字符串中字符为1,并且用num统计1的个数cnt之后再扫描长字符串的每个字符,若原来hashTable[*pHashKey]为1时,我们修改为0,且将cnt减去1,否则不处理;若cnt == 0或者扫描结束,循环结束;
伪代码:
bool is_contain(string LongStr, string ShortStr)
{
bool is_pipei = false;
const int tableSize = 256;
unsigned int hashTable[tableSize];
int i,cnt = 0;
for(i = 0; i < tableSize; i++)
{
hashTable[i] = 0;
}
for(i = 0; i < ShortStr.length(); i++)
{
hashTable[i] = 1;
cnt ++;
}
for(i = 0; i < LongStr.length(); i++)
{
if(hashTable[LongStr[i]] == 1)
{
hashTable[LongStr[i]] = 0;
cnt --;
}
}
if(cnt == 0)
{
is_pipei = true;
}
return is_pipei;
}
思路2:其实可以根据存储长的字符串,对于哈希表值为1,之后再去扫描短字符串中的每个字符,如果存在哈希表值为0,则说明短字符中存在长字符中不含有的字符,反之不存在。
伪代码:
bool is_contain(char *src, char *des)
{
bool is_pipei = true; //创建一个哈希表,并初始化
const int tableSize = 256;
int hashTable[tableSize];
int len1,len2,i;
for(i=0; i< tableSize; i++)
hashTable[i] = 0;
len1 = strlen(src);
for(i=0; i< len1; i++)
hashTable[src[i]] = 1;
len2 = strlen(des);
for(i=0; i< len2; i++)
{
if(hashTable[des[i]] == 0)
is_pipei = false; //匹配失败
}
return is_pipei; //匹配成功
}
思路3、先轮询长字符串,用位运算计算出一个”签名“,再用短字符串中字符去长字符串中去查找
//最好的方法,时间复杂度O(n+m) 空间复杂度为O(1)bool StringContain(string &a, string &b)
{
int hash = 0;
for(int i = 0; i < a.length(); ++i)
{
hash |= (1 << (a[i] - ‘A‘));
}
for(int i = 0; i < b.length(); ++i)
{
if((hash & (1 << (b[i] - ‘A‘))) == 0)
return false;
}
return true;
}
这种方法实质上是用整数替代了哈希表,时间复杂度O(n+m) 空间复杂度为O(1)
最优质的算法