字符串是否包含问题

转载于:http://blog.csdn.net/v_july_v/article/details/6347454

第一节、一道俩个字符串是否包含的问题
  1.1
On*m)的轮询方法
  1.2
O(mlogm)+O(nlogn)+O(m+n)的排序方法
  1.3
On+m)的计数排序方法
第二节
  2.1
On+m)的hashtable的方法
  2.2
On+m)的数组存储方法
第三节、On)到On+m)的素数方法


第一节、一道俩个字符串是否包含的问题
1.0、题目描述:
假设这有一个各种字母组成的字符串,假设这还有另外一个字符串,而且这个字符串里的字母数相对少一些。从算法是讲,什么方法能最快的查出所有小字符串里的字母在大字符串里都有?

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


1.1On*m)的轮询方法

判断string2中的字符是否在string1?
String 1: ABCDEFGHLMNOPQRS
String 2: DCGSRQPOM

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

    假设n是字符串string1的长度,m是字符串string2的长度,那么此算法,需要On*m)次操作,拿上面的例子来说,最坏的情况下将会有16*8 = 128次操作

    我们不难写出以下代码:

1.   #include <iostream>  

2.   using namespace std;  

3.     

4.   int CompareSting(string LongSting,string ShortSting)  

5.   {  

6.       for (int i=0; i<ShortString.length(); i++)  

7.       {  

8.           for (int j=0; j<LongString.length(); j++)  //O(n*m)  

9.           {  

10.              if (LongString[i] == ShortString[j])  //一一比较  

11.              {  

12.                  break;  

13.              }  

14.                

15.          }  

16.          if (j==LongString.length())  

17.          {  

18.              cout << "false" << endl;  

19.              return 0;  

20.          }  

21.      }  

22.      cout << "true" << endl;  

23.      return 1;  

24.  }  

25.    

26.  int main()   

27.  {   

28.      string LongString="ABCDEFGHLMNOPQRS";  

29.      string ShortString="DCGSRQPOM";  

30.      compare(LongString,ShortString);  

31.      return 0;  

32.  }    

 上述代码的时间复杂度为On*m),显然,时间开销太大,我们需要找到一种更好的办法。

(网友acs713在本文评论下指出:个人的代码风格不规范,的确如此,后来看过<<代码大全>>之后,此感尤甚。个人会不断完善和规范此类代码风格。有任何问题,欢迎随时指正。谢谢大家。)

 

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

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

    关于采用何种排序方法,我们采用最常用的快速排序,下面的快速排序的代码用的是以前写的,比较好懂,并且,我执意不用库函数的qsort代码。唯一的问题是,此前写的代码是针对整数进行排序的,不过,难不倒我们,稍微改一下参数,即可,如下:

1.   //copyright@ 2011 July && yansha  

2.   //Julyupdated2011.04.23.  

3.   #include <iostream>  

4.   #include <string>  

5.   using namespace std;  

6.     

7.   //以前的注释,还让它保留着  

8.   int partition(string &str,int lo,int hi)   

9.   {  

10.      int key = str[hi];      //以最后一个元素,data[hi]为主元  

11.      int i = lo - 1;  

12.      for(int j = lo; j < hi; j++) ///注,jp指向的是r-1,不是r  

13.      {  

14.          if(str[j] <= key)  

15.          {  

16.              i++;  

17.              swap(str[i], str[j]);  

18.          }  

19.      }  

20.      swap(str[i+1], str[hi]);    //不能改为swap(&data[i+1],&key)  

21.      return i + 1;   

22.  }  

23.    

24.  //递归调用上述partition过程,完成排序。  

25.  void quicksort(string &str, int lo, int hi)  

26.  {  

27.      if (lo < hi)  

28.      {  

29.          int k = partition(str, lo, hi);  

30.          quicksort(str, lo, k - 1);  

31.          quicksort(str, k + 1, hi);  

32.      }  

33.  }  

34.    

35.  //比较,上述排序O(m log m) + O(n log n),加上下面的O(m+n)  

36.  //时间复杂度总计为:O(mlogm)+O(nlogn)+O(m+n)  

37.  void compare(string str1,string str2)  

38.  {  

39.      int posOne = 0;  

40.      int posTwo = 0;  

41.      while (posTwo < str2.length() && posOne < str1.length())  

42.      {  

43.          while (str1[posOne] < str2[posTwo] && posOne < str1.length() - 1)  

44.              posOne++;  

45.          //如果和str2相等,那就不能动。只有比str2小,才能动。  

46.            

47.          if (str1[posOne] != str2[posTwo])  

48.              break;  

49.            

50.          //posOne++;     

51.          //归并的时候,str1[str1Pos] == str[str2Pos]的时候,只能str2Pos++,str1Pos不可以自增。  

52.          //多谢helloword指正。  

53.    

54.          posTwo++;  

55.      }  

56.                    

57.      if (posTwo == str2.length())  

58.          cout << "true" << endl;  

59.      else  

60.          cout << "false" << endl;  

61.  }  

62.    

63.  int main()   

64.  {   

65.      string str1 = "ABCDEFGHLMNOPQRS";  

66.      string str2 = "DCGDSRQPOM";    

67.      //之前上面加了那句posOne++之所以有bug,是因为,@helloword  

68.      //因为str1如果也只有一个D,一旦posOne++,就到了下一个不是'D'的字符上去了,  

69.      //str2有俩DposTwo++后,下一个字符还是'D',就不等了,出现误判。  

70.    

71.      quicksort(str1, 0, str1.length() - 1);  

72.      quicksort(str2, 0, str2.length() - 1);  //先排序  

73.      compare(str1, str2);                    //后线性扫描  

74.      return 0;  

75.  }  

    

第二节、寻求线性时间的解法
2.1On+m)的hashtable的方法
    上述方案中,较好的方法是先对字符串进行排序,然后再线性扫描,总的时间复杂度已经优化到了:Om+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,并将m1;若hash[a] == 0,则不做处理 
 4
、若m == 0 or 扫描结束,退出循环。

    代码实现,也不难,如下:

1.   //copyright@ 2011 yansha  

2.   //Julyupdated2011.04.25   

3.   #include <iostream>  

4.   #include <string>  

5.   using namespace std;  

6.     

7.   int main()  

8.   {  

9.       string str1="ABCDEFGHLMNOPQRS";  

10.      string str2="DCGSRQPOM";  

11.    

12.      // 开辟一个辅助数组并清零  

13.      int hash[26] = {0};  

14.    

15.      // num为辅助数组中元素个数  

16.      int num = 0;  

17.    

18.      // 扫描短字符串  

19.      for (int j = 0; j < str2.length(); j++)  

20.      {  

21.          // 将字符转换成对应辅助数组中的索引  

22.          int index = str1[j] - 'A';  

23.    

24.          // 如果辅助数组中该索引对应元素为0,则置1,且num++;  

25.          if (hash[index] == 0)  

26.          {  

27.              hash[index] = 1;  

28.              num++;  

29.          }  

30.      }  

31.    

32.      // 扫描长字符串  

33.      for (int k = 0; k < str1.length(); k++)  

34.      {  

35.          int index = str1[k] - 'A';  

36.    

37.          // 如果辅助数组中该索引对应元素为1,则num--;为零的话,不作处理(不写语句)。  

38.          if(hash[index] ==1)  

39.          {  

40.              hash[index] = 0;  

41.              num--;  

42.              if(num == 0)    //m==0,即退出循环。  

43.                  break;  

44.          }  

45.      }  

46.    

47.      // num0说明长字符串包含短字符串内所有字符  

48.      if (num == 0)  

49.          cout << "true" << endl;  

50.      else  

51.          cout << "false" << endl;  

52.      return 0;  

53.  }  

 

2.2On+m)的数组存储方法

    有两个字符串short_strlong_str
   
第一步:你标记short_str中有哪些字符,在store数组中标记为true(store数组起一个映射的作用,如果有A,则将第1个单元标记true,如果有B,则将第2个单元标记true... 如果有Z, 则将第26个单元标记true
   
第二步:遍历long_str,如果long_str中的字符包括short_str中的字符则将store数组中对应位置标记为false(如果有A,则将第1个单元标记false,如果有B,则将第2个单元标记false... 如果有Z, 则将第26个单元标记false),如果没有,则不作处理。
   
第三步:此后,遍历store数组,如果所有的元素都是false,也就说明store_str中字符都包含在long_str内,输出true。否则,输出false

    举个简单的例子好了,如abcdabcdefg俩个字符串,
    1
、先遍历短字符串abcd,在store数组中想对应的abcd的位置上的单元元素置为true
    2
、然后遍历abcdefg,在store数组中相应的abcd位置上,发现已经有了abcd,则前4个的单元元素都置为false,当我们已经遍历了4个元素,等于了短字符串abcd4个数目,所以,满足条件,退出。
   
(不然,继续遍历的话,我们会发现efgstore数组中没有元素,不作处理。最后,自然,就会发现store数组中的元素单元都是false的。)
    3
、遍历store数组,发现所有的元素都已被置为false,所以程序输出true

    其实,这个思路和上一节中,On+m)的hashtable的方法代码,原理是完全一致的,且本质上都采用的数组存储(hash表也是一个数组),但我并不认为此思路多此一举,所以仍然贴出来。ok,代码如下:

1.   //copyright@ 2011 Hession  

2.   //Julyupdated2011.04.23.  

3.   #include<iostream>  

4.   #include<string.h>  

5.   using namespace std;  

6.     

7.   int main()  

8.   {  

9.       char long_ch[]="ABCDEFGHLMNOPQRS";  

10.      char short_ch[]="DEFGHXLMNOPQ";  

11.      int i;  

12.      bool store[58];  

13.      memset(store,false,58);    

14.        

15.      //前两个   遍历 两个字符串后面一个是  遍历 数组  

16.      for(i=0;i<sizeof(short_ch)-1;i++)  

17.          store[short_ch[i]-65]=true;  

18.        

19.      for(i=0;i<sizeof(long_ch)-1;i++)  

20.      {  

21.          if(store[long_ch[i]-65]!=false)  

22.              store[long_ch[i]-65]=false;  

23.      }  

24.      for(i=0;i<58;i++)  

25.      {  

26.          if(store[i]!=false)  

27.          {  

28.              cout<<"short_ch is not in long_ch"<<endl;  

29.              break;    

30.          }          

31.          if(i==57)  

32.              cout<<"short_ch is in long_ch"<<endl;  

33.      }  

34.        

35.      return 0;  

36.  }  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值