首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

程序员编程艺术:第二章、字符串是否包含问题

  • 25-03-02 17:20
  • 4406
  • 7566
blog.csdn.net

                        程序员编程艺术:第二章、字符串是否包含问题


作者:July,yansha,caopengcs。
时间:二零一一年四月二十三日。
致谢:老梦,nossiac,Hession,Oliver,luuillu,啊菜,雨翔,及微软100题实现小组所有成员。


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

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

    点评:
    1、题目描述虽长,但题意简单明了,就是给定一长一短的俩个字符串A,B,假设A长B短,现在,要你判断B是否包含在字符串A中,即B?(-A。

    2、题意虽简单,但实现起来并不轻松,且当如果面试官步步紧逼,一个一个否决你能想到的方法,要你给出更好、最好的方案时,你恐怕就要伤不少脑筋了。

    ok,在继续往下阅读之前,您最好先想个几分钟,看你能想到的最好方案是什么,是否与本文最后实现的方法一致。


1.1、O(n*m)的轮询方法

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

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

    代码可如下编写:

  1. //copyright@啊菜 2011
  2. //updated@July&Image丶时光 2013
  3. #include
  4. #include
  5. using namespace std;
  6. int CompareString(string LongString,string ShortString)
  7. {
  8. int i,j;
  9. for (i=0; ilength(); i++)
  10. {
  11. for (j=0; jlength(); j++) //O(n*m)
  12. {
  13. if (LongString[j] == ShortString[i]) //一一比较
  14. {
  15. break;
  16. }
  17. }
  18. if (j==LongString.length())
  19. {
  20. cout << "false" << endl;
  21. return 0;
  22. }
  23. }
  24. cout << "true" << endl;
  25. return 1;
  26. }
  27. int main()
  28. {
  29. string LongString="ABCDEFGHLMNOPQRS";
  30. string ShortString="DCGSRQPO";
  31. CompareString(LongString,ShortString);
  32. return 0;
  33. }

    假设n是字符串string1的长度,m是字符串string2的长度,那么此算法,需要O(n*m)次操作,拿上面的例子来说,最坏的情况下将会有16*8 = 128次操作。显然,时间开销太大,我们需要找到一种更好的办法。

 

1.2、O(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.3、O(n+m)的计数排序方法

    此方案与上述思路相比,就是在排序的时候采用线性时间的计数排序方法,排序O(n+m),线性扫描O(n+m),总计时间复杂度为:O(n+m)+O(n+m)=O(n+m)。

    代码如下:

 不过上述方法,空间复杂度为O(n+m),即消耗了一定的空间。有没有在线性时间,且空间复杂度较小的方案列?

 

第二节、寻求线性时间的解法
2.1、O(n+m)的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 扫描结束,退出循环。

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

 

2.2、O(n+m)的数组存储方法

    有两个字符串short_str和long_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。

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

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

 

第三节、O(n)到O(n+m)的素数方法

    我想问的是,还有更好的方案么?
    你可能会这么想:O(n+m)是你能得到的最好的结果了,至少要对每个字母至少访问一次才能完成这项操作,而上一节最后的俩个方案是刚好是对每个字母只访问一次。

    ok,下面给出一个更好的方案:
    假设我们有一个一定个数的字母组成字串,我给每个字母分配一个素数,从2开始,往后类推。这样A将会是2,B将会是3,C将会是5,等等。现在我遍历第一个字串,把每个字母代表的素数相乘。你最终会得到一个很大的整数,对吧?
    然后——轮询第二个字符串,用每个字母除它。如果除的结果有余数,这说明有不匹配的字母。如果整个过程中没有余数,你应该知道它是第一个字串恰好的子集了。

思路总结如下:
1.定义最小的26个素数分别与字符'A'到'Z'对应。
2.遍历长字符串,求得每个字符对应素数的乘积。
3.遍历短字符串,判断乘积能否被短字符串中的字符对应的素数整除。
4.输出结果。

    至此,如上所述,上述算法的时间复杂度为O(m+n),时间复杂度最好的情况为O(n)(遍历短的字符串的第一个数,与长字符串素数的乘积相除,即出现余数,便可退出程序,返回false),n为长字串的长度,空间复杂度为O(1)。如你所见,我们已经优化到了最好的程度。

    不过,正如原文中所述:“现在我想告诉你 —— Guy的方案在算法上并不能说就比我的好。而且在实际操作中,你很可能仍会使用我的方案,因为它更通用,无需跟麻烦的大型数字打交道。但从”巧妙水平“上讲,Guy提供的是一种更、更、更有趣的方案。”

    ok,如果你有更好的思路,欢迎在本文的评论中给出,非常感谢。

     

上述程序待改进的地方:
1.只考虑大写字符,如果考虑小写字符和数组的话,素数数组需要更多素数
2.没有考虑重复的字符,可以加入判断重复字符的辅助数组。

大整数除法的代码,后续公布下载地址。

    说明:此次的判断字符串是否包含问题,来自一位外国网友提供的gofish、google面试题,这个题目出自此篇文章:http://www.aqee.net/2011/04/11/google-interviewing-story/,文章记录了整个面试的过程,比较有趣,值得一读。

    扩展:正如网友安逸所说:其实这个问题还可以转换为:a和b两个字符串,求b串包含a串的最小长度。包含指的就是b的字串包含a中每个字符。


updated:我们假设字母都由大写字母组成……,我们先对小字符串预处理,可以得到B里包含哪些字符,这里可以用位运算,或者用bool数组。位运算简单些,用一个int中的26bit表示其是否在B中出现即可。

  1. //copyright@ caopengcs 2013
  2. bool AcontainsB(char *A,char *B) {
  3. int have = 0;
  4. while (*B) {
  5. have |= 1 << (*(B++) - 'A'); // 把A..Z对应为0..26
  6. }
  7. while (*A) {
  8. if ((have & (1 << (*(A++) - 'A'))) == 0) {
  9. return false;
  10. }
  11. }
  12. return true;
  13. }
    本文编程艺术第二章github地址: https://github.com/julycoding/The-Art-Of-Programming_by-July/blob/master/ebook/zh/02.0.md,欢迎fork,欢迎贡献。完。

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览49583 人正在系统学习中
注:本文转载自blog.csdn.net的v_JULY_v的文章"http://blog.csdn.net/v_JULY_v/article/details/6347454#"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top