第二章_字符串是否包含问题

题目描述:
假设这有一个各种字母组成的字符串A,和另外一个字符串B,字符串里B的字母数相对少一些。什么方法能最快的查出所有小字符串B里的字母在大字符串A里都有?

比如,如果是下面两个字符串:
String 1: ABCDEFGHLMNOPQRS
String 2: DCGSRQPO
答案是true,所有在string2里的字母string1也都有。
 
如果是下面两个字符串:  
String 1: ABCDEFGHLMNOPQRS  
String 2: DCGSRQPZ  
答案是false,因为第二个字符串里的Z字母不在第一个字符串里。

解法一、暴力轮询

判断string2中的字符是否在string1中?:

String 1: ABCDEFGHLMNOPQRS

String 2: DCGSRQPO

最直观也是最简单的思路是,针对string2中每一个字符,一一与string1中每个字符依次轮询比较,看它是否在String1中。

代码可如下编写:

#include<iostream>
#include <string>
using namespace std;

bool Compare(const string &s1,const string &s2)//s1为长,s2为短
{   

	for (string::const_iterator i=s2.begin();i!=s2.end();++i)
	{
		string::const_iterator j;
		for (j=s1.begin();j!=s1.end();++j)
		{
			if (*i==*j)
			{
				break;
			}
		}
		if (j==s1.end())
		{
			return false;
		}
	}
return true;

} 

int main()
{
	string str1,str2;
	cout<<"Please input the string(str1 and str2):str1 is longer than str2!!!!!"<<endl;
	cin>>str1>>str2;
    cout<<"str1: "<<str1<<endl;
	cout<<"str2: "<<str2<<endl;

	if (Compare(str1,str2))
	{
		cout<<"str2 is included in str1"<<endl;
	} 
	else
	{
		cout<<"str2 is not included in str1"<<endl;
	}

	system("pause");
	return 0;
}


假设n是字符串String1的长度,m是字符串String2的长度,那么此算法,需要O(n*m)次操作,以上面的例子来说,将会有16*8 = 128次操作。显然,时间开销太大,应该找到一种更好的办法。


解法二、普通排序

一个稍微好一点的方案是先对这两个字符串的字母进行排序,然后同时对两个字串依次轮询。两个字串的排序需要(常规情况)O(m log m) + O(n log n)次操作,之后的线性扫描需要O(m+n)次操作。

同样拿上面的字串做例子,将会需要16*4 + 8*3 = 88,再加上对两个字串线性扫描的16 + 8 = 24的操作。(随着字串长度的增长,你会发现这个算法的效果会越来越好)

关于排序方法,可采用最常用的快速排序,C有库函数qsort,C++有库函数sort,排序不是重点,因此使用库函数好一些。

#include<iostream>
#include <string>
#include <algorithm>
using namespace std;

bool Compare(string str_1,string str_2)//s1为长,s2为短
{   
	string s1=str_1;
	string s2=str_2;
	sort(s1.begin(),s1.end());
	sort(s2.begin(),s2.end());
	for (string::iterator i=s2.begin(),j=s1.begin();i!=s2.end();++i)
	{
	
			while (j!=s1.end()&&*i>*j)//while (*i>*j&&j!=s1.end())会出错,因为当j==s1.end()时,首先执行*j,而此时j已经是一个空指针了
			{					
					++j;						
			} 

			if (j==s1.end()||*i<*j)
			{
				return false;
			} 
	}
	return true;

} 

int main()
{
	string str1,str2;
	cout<<"Please input the string(str1 and str2):str1 is longer than str2!!!!!"<<endl;
	cin>>str1>>str2;
    cout<<"str1: "<<str1<<endl;
	cout<<"str2: "<<str2<<endl;
	bool flag=Compare(str1,str2);
	if (flag)
	{
		cout<<"str2 is included in str1"<<endl;
	} 
	else
	{
		cout<<"str2 is not included in str1"<<endl;
	}

	system("pause");
	return 0;
}

解法三、计数比较

此方案与上述相比,区别在于不用排序。采用线性时间的计数方法,假设需要比较字符串A(n)中是否包含字符串B(m),统计A中出现的字符O(n),比较B中是否有出现的字符O(m),总计时间复杂度为:O(n+m)。

代码如下:

#include<iostream>
#include<vector>
#include <string>
using namespace std;

bool Compare(string s1,string s2)//s1为长,s2为短
{   
    vector<int> have;
	have.resize(26);
	for (string::iterator i=s1.begin();i!=s1.end();++i)
	{
		have[*i-'A']++;
	}

	for (string::iterator j=s2.begin();j!=s2.end();++j)
	{
		if (have[*j-'A']--==0)//if (have[*j-'A']==0)
		{
			return false;
		}
	}
	return true;
} 

int main()
{
	string str1,str2;
	cout<<"Please input the string(str1 and str2):str1 is longer than str2!!!!!"<<endl;
	cin>>str1>>str2;
    cout<<"str1: "<<str1<<endl;
	cout<<"str2: "<<str2<<endl;
	bool flag=Compare(str1,str2);
	if (flag)
	{
		cout<<"str2 is included in str1"<<endl;
	} 
	else
	{
		cout<<"str2 is not included in str1"<<endl;
	}

	system("pause");
	return 0;
}

解法四、巧用hashtable

上述方案中,较好的方法是先对字符串进行排序,然后再线性扫描,总的时间已经优化到了:O(m+n),貌似到了极限,还有没有更好的办法?

可以对短字符串进行轮询(此思路的叙述可能与网上的一些叙述有出入。最好是把短的先存储,那样,会降低时间复杂度),把其中的每个字母都放入一个Hashtable里(始终设m为短字符串的长度,那么此项操作成本是O(m)或8次操作)。然后轮询长字符串,在Hashtable里查询短字符串的每个字符,看能否找到。如果找不到,说明没有匹配成功,轮询长字符串将消耗掉16次操作,这样两项操作加起来一共只有8+16=24次。

当然,理想情况是如果长字串的前缀就为短字串,只需消耗8次操作,这样总共只需8+8=16次。

或如梦想天窗所说: 我之前用散列表做过一次,算法如下: 1. hash[26],先全部清零,然后扫描短的字符串,若有相应的置1, 2. 计算hash[26]中1的个数,记为m 3. 扫描长字符串的每个字符a;若原来hash[a] == 1 ,则修改hash[a] = 0,并将m减1;若hash[a] == 0,则不做处理 4. 若m == 0 or 扫描结束,退出循环。


 

#include<iostream>
#include<vector>
#include <string>
using namespace std;

bool Compare(string s1,string s2)//s1为长,s2为短
{   
    vector<int> have;
	int m=0;
	have.resize(26);
	for (string::iterator i=s2.begin();i!=s2.end();++i)
	{
		int n=*i-'A';
		if (have[n]==0)
		{
			have[n]=1;
			m++;
		}		
	}

	for (string::iterator j=s1.begin();j!=s1.end();++j)
	{
		int n=*j-'A';
		if (have[n]==1)
		{
			have[n]=0;
			m--;
		}
	}
	if (m==0)
	{
		return true;
	} 
	else
	{
		return false;
	}
} 

int main()
{
	string str1,str2;
	cout<<"Please input the string(str1 and str2):str1 is longer than str2!!!!!"<<endl;
	cin>>str1>>str2;
    cout<<"str1: "<<str1<<endl;
	cout<<"str2: "<<str2<<endl;
	bool flag=Compare(str1,str2);
	if (flag)
	{
		cout<<"str2 is included in str1"<<endl;
	} 
	else
	{
		cout<<"str2 is not included in str1"<<endl;
	}

	system("pause");
	return 0;
}

解法六、位运算

最好的思路是对字符串A,用位运算(26bit整数表示)计算出一个“签名”,再用B中的字符到A里面进行查找。这个方法的实质是用一个整数代替了hashtable,空间复杂度可以降低为O(1)。时间复杂度还是O(n + m)。


 

#include<iostream>
#include<vector>
#include <string>
using namespace std;

bool Compare(string s1,string s2)//s1为长,s2为短
{   
    int have=0;
	for (string::iterator i=s1.begin();i!=s1.end();++i)
	{
		int n=*i-'A';
		have|=1<<n;
		
	}

	for (string::iterator j=s2.begin();j!=s2.end();++j)
	{
		int n=*j-'A';
		if ((have&(1<<n))==0)//若s1为ABC, 而s2为AAB,则 return false;
		{
			return false;
		}
	}

		return true;

} 

int main()
{
	string str1,str2;
	cout<<"Please input the string(str1 and str2):str1 is longer than str2!!!!!"<<endl;
	cin>>str1>>str2;
    cout<<"str1: "<<str1<<endl;
	cout<<"str2: "<<str2<<endl;
	bool flag=Compare(str1,str2);
	if (flag)
	{
		cout<<"str2 is included in str1"<<endl;
	} 
	else
	{
		cout<<"str2 is not included in str1"<<endl;
	}

	system("pause");
	return 0;
}


类似问题

1、一个单词单词字母交换,可得另一个单词,如army->mary,成为兄弟单词。提供一个单词,在字典中找到它的兄弟。描述数据结构和查询过程。

2、如果两个字符串的字符一样,但是顺序不一样,被认为是兄弟字符串,问如何在迅速匹配兄弟字符串(如,bad和adb就是兄弟字符串)。 思路:判断各自素数乘积是否相等。

3、假设两个字符串中所含有的字符和个数都相同我们就叫这两个字符串匹配,比如:abcda和adabc,由于出现的字符个数都是相同,只是顺序不同,所以这两个字符串是匹配的,要求高效。


 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值