面试题精解之二: 字符串、数组(1)

面试题精解之二: 字符串、数组(1)

 

本篇文章发表在下面三个博客中,如果出现排版问题,请移步到另一个博客。

http://www.cppblog.com/flyinghearts

http://www.cnblogs.com/flyinghearts

http://blog.csdn.net/flyinghearts

 

1     在一个字符串中找到第一个只出现一次的字符,如输入abac,则输出b。

2     输出字符串的所有组合,如"abc"输出a、b、c、ab、ac、bc、abc。

3     根据条件找出两个数。

4     求数组(或环状数组)的最大连续(或不连续)子序列和。

 

      1     在一个字符串中找到第一个只出现一次的字符,如输入abac,则输出b。

本题看似很简单,开个长度为256的表,对每个字符hash计数就可以了,但很多人写的代码都存在bug,可能会发生越界访问。这是C/C++语言上的一个陷阱,C/C++中的char有三种类型:char、signed char和unsigned char。char类型的符号是由编译器指定的,一般是有符号的。在对字符进行hash时,应该先将字符转为无符号类型,不然,下标为负值时,就会出现越界访问。

    另外,可以用一个cache数组,记录当前找到的只出现一次的字符,避免对原字符串进行第二次遍历。

 

char get_first_only_one(const char str[])

{

  if (str == NULL)  return 0;

  const int table_size = 256;             //最好写成: 1 << CHAR_BIT 或 UCHAR_MAX + 1

  unsigned  count[table_size] = { 0};

  char      cache[table_size];

  char *q = cache;

 

  for (const char* p = str;  *p != 0; ++p)

    if (++count[(unsigned char)*p] == 1)  *q++ = *p;  //要先转成无符号数!!!

   

  for (const char* p = cache; p < q; ++p)

    if (count[(unsigned char)*p] == 1)  return *p;

   

  return 0;

}

 

      2     输出字符串的所有组合,如"abc"输出a、b、c、ab、ac、bc、abc。

本题假定字符串中的所有字符都不重复。根据题意,如果字符串中有n个字符,那么总共要输出2^n – 1种组合。这也就意味着n不可能太大,否则的话,以现在CPU的运算速度,程序运行一次可能需要跑几百年、几千年,而且也没有那么大的硬盘来储存运行结果。因而,可以假设n小于一个常数(比如64)。

本题最简洁的方法,应该是采用递归法。遍历字符串,每个字符只能取或不取。取该字符的话,就把该字符放到结果字符串中,遍历完毕后,输出结果字符串。

n不是太小时,递归法效率很差(栈调用次数约为2^n,尾递归优化后也有2^(n-1))。注意到本题的特点,可以构照一个长度为n的01字符串(或二进制数)表示输出结果中最否包含某个字符,比如:"001"表示输出结果中不含字符a、b,只含c,即输出结果为c,而"101",表示输出结果为ac。原题就是要求输出"001"到"111"这2^n – 1个组合对应的字符串。

 

 

//迭代法

void all_combine(const char str[])

{

  if (str == NULL || *str == 0) return;

  const size_t max_len = 64;

 

  size_t len =  strlen(str);

  if (len >= max_len ) {

    puts("输入字符串太长。\n你愿意等我一辈子吗?");

    return;

  }

 

  bool used[max_len] = { 0};    //可以用一个64位无符号数表示used数组

  char cache[max_len];

  char *result = cache + len;

  *result = 0;

 

  while (true) {

    size_t idx = 0;

    while (used[idx]) {      //模拟二进制加法,一共有2^len – 1个状态

      used[idx] = false;

      ++result;

      if (++idx == len)  return;

    }

    used[idx] = true;

    *--result = str[idx];

    puts(result); 

  }

}

 

 

//递归解法

static void all_combine_recursive_impl(const char* str, char* result_begin, char* result_end)

{

  if (*str == 0) {

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值