《编程之法》1.2字符串的包含

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qibofang/article/details/51912586

题目描述:字符串的包含:给定长字符串A和短字符串B,判断B的字符是否全部包含在A中(若字符串中字符重复,只考虑一次该字符,如asdff包含ffff)。如给定A:asdffds,若B为:asddf,输出true;若B为:abds,输出false。

解法一: 蛮力轮询
轮询B中每个字符c,然后用该字符c去依次比较A中的每一个字符,最后看是否B中的字符都在A中。若A,B字符串的串长分别为n和m,则时间复杂度O(mn),空间复杂度为O(0)。

#include <iostream>
#include <string>
using namespace std;
bool StringContain(string &a, string &b){
	int i, j;
	for(i = 0; i < b.size(); ++i){
		for(j = 0; j < a.size() && b[i] != a[j]; ++j) ;
		if(j == a.size())
			return false;
	}
	return true;
}

int main(){
	string a, b;
	while(cin >> a >> b){ 
		if(StringContain(a, b)) 
			cout << "true\n"; //不能使用条件语句
		else 
			cout << "false\n";
	}
	return 0;
}

解法二: 排序后轮询
使用sort进行字典序排序后,设置两个指针分别指向A,B的起始处,若A的当前元素值小于B的当前元素值,则多次(或一次)移动A的指针到相等元素处;若A的当前元素值大于B的当前元素值,说明A中没有B字符串的当前元素,直接输出false;若A的当前元素值等于B的当前元素值相等,则只将B的指针向前移动一次。完整轮询完B后输出true。时间复杂度为O(mlogm)+O(nlogn)+O(m+n),空间复杂度为O(0)。

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
bool StringContain(string &a, string &b){
	int pa, pb;
	sort(a.begin(), a.end()); //注意sort对于容器的使用方式
	sort(b.begin(), b.end());
	for(pa = 0, pb = 0; pa < a.size() && pb < b.size(); ){
		while(pa < a.size() && a[pa] < b[pb]) pa++;
		if(pa == a.size() || a[pa] > b[pb])
			return false;
		pb++;
	}
	return true;
}

int main(){
	string a, b;
	while(cin >> a >> b){ 
		if(StringContain(a, b)) 
			cout << "true\n"; //不能使用条件语句
		else 
			cout << "false\n";
	}
	return 0;
}


解法三:素数相乘

若字符串A,B只含有26个大写字母中一些字符,则可使用素数2代替A,素数3代替B,素数5代替C,......,素数101代替Z。将字符串A的所有字符所对应的素数相乘得到乘积M,由于质数不可分解,对于B中每个字符所对应的质数,若字符串A包含该字符,那么乘积M肯定能整除于该质数,若不包含则不能被整除。时间复杂度为O(n+m),空间复杂度为O(1)。但这种方法不适合非大写字符,以及字符串中包含不同字符过多(素数相乘造成溢出)的情况。

#include <iostream>
#include <string>
using namespace std;
bool StringContain(string &a, string &b){
	const 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, 97, 101};
	int mul = 1, i;
	for(i = 0; i < a.size(); ++i){
		if(mul % prime[a[i]-'A'])
			mul *= prime[a[i]-'A'];
	}
	for(i = 0; i < b.size(); ++i){
		if(mul % prime[b[i]-'A'])
			return false;
	}
	return true;
}

int main(){
	string a, b;
	while(cin >> a >> b){ 
		if(StringContain(a, b)) 
			cout << "true\n"; //不能使用条件语句
		else 
			cout << "false\n";
	}
	return 0;
}

解法四:位运算符
若字符串A,B只含有26个大写字母中一些字符。可以使用散列表,对字符串A中的每个字符c,令hash[c-'A']=1;,之后遍历B中的每个元素时,只需判断对应的hash[c-'B']是否为真即可。这样的时间复杂度为O(n),空间复杂度也为O(n)。
对于只含26位大写字母的字符串A,我们可以只使用一个int型变量hash作为hash表,这是因为int变量有32bit,若字符串A中含有字符A,则令变量hash的第1位置1,若字符串A中含有y,则令变量hash的第25位置1。对于字符串B中任一字符,只需判断该字符在变量hash中所对应的位是否为1。这样时间复杂度为O(m+n),空间复杂度为O(1)。

#include <iostream>
#include <string>
using namespace std;
bool StringContain(string &a, string &b){
	int hash = 0, i;
	for(i = 0; i < a.size(); ++i)
		hash = hash | (1 << (a[i] - 'A')); //将1的二进制左移(a[i] - 'A')位
	for(i = 0; i < b.size(); ++i)
		if((hash & (1 << (b[i] - 'A'))) == 0) //下面有优先级表,==运算符的优先级大于&运算符,因此&的两边要加()
			return false;
	return true;
}

int main(){
	string a, b;
	while(cin >> a >> b){ 
		if(StringContain(a, b)) 
			cout << "true\n"; //不能使用条件语句
		else 
			cout << "false\n";
	}
	return 0;
}



举一反三:
变位词:如果两个字符串中字符一样,出现次数也一样,只是出现的顺序不一样,则称这两个字符串为兄弟字符串,例如"bad"和"adb"
解法1:sort排序后依次比较,若为兄弟字符串,则排序后的对应下标和对应下标元素时时相等,时间复杂度为O(nlogn)+O(n)=O(nlogn);
解法2:对第一个字符串使用hash表,则hash表中存放的某字符的个数。对第二个字符串进行遍历时,依次将对应hash表中的元素减1,若为兄弟字符串,则最后hash表中元素应该全为0,时间复杂度为O(n),空间复杂度为O(n)。



猜你喜欢

转载自blog.csdn.net/qibofang/article/details/51912586