检查字符串是不是“平衡”的递归函数

Posted

技术标签:

【中文标题】检查字符串是不是“平衡”的递归函数【英文标题】:Recursive Function that checks if a string is "Balanced"检查字符串是否“平衡”的递归函数 【发布时间】:2015-03-19 03:09:15 【问题描述】:

我有一个问题,我必须只使用递归来编写算法(不得使用循环)。 问题是我的函数应该检查给定字符串是否“平衡”。 该字符串仅包含字母(无符号)并且仅包含 ("[" , "]") 那些括号。 (例如:“[aa][abbsa]”)。

假设每个“左括号”(“[”)都有一个右括号(“]”),换句话说,字符串中的括号是平衡的并且没有必要检查。 字符串始终是以下两种格式之一:

    简单字符串: CHARACTERS。

它只包含没有括号的字符。 (例如:“aaabbcc”)。

    带有 2 个子字符串的字符串:

[][]

LEFT :它本身是一个子字符串,实际上可以是两种格式(简单字符串或带有 2 个子字符串的字符串)

RIGHT :它本身是一个子字符串,实际上可以是两种格式(简单字符串或带有 2 个子字符串的字符串)

编辑: 该字符串是有效的,不需要检查它是否合法。 它始终是上述格式和示例之一(也可能更复杂,但始终是合法的)。

编辑: 字符串只能是第一种格式或第二种格式。如果是第二种格式,则包含第一种格式,必须以“[”开头,以“]”结尾。 示例:“aaabbbb”(第一种格式)。 “[aa][bbbb]”(第二种格式)。 “[[aa][b]][[[a][bbb]][aaaa]]”(第二种格式)。

如果字符串至少满足以下条件之一,则该字符串是平衡的:

    字符串来自第一种格式。

    字符串来自第二种格式,并且左侧的字符数(不带括号)(我们称之为权重)是偶数,右侧的权重也是如此。

    字符串来自第二种格式,左侧的权重为奇数,右侧的权重也是如此。

例子:

字符串“[abcde][xyz]”是平衡的,因为右权重和左权重都是奇数。

字符串“[abcde][xyzw]”不平衡,因为右权重是偶数(4 是偶数),左权重是奇数(5 是奇数)。 p>

字符串“[abcdef][[x][yzw]]”是平衡的。 左侧重量为 6。 子字符串“[x][yzw]”是平衡的。 (左权重为 1,右权重为 3(均为奇数)。 “[x][yzw]”的权重为4。 因此,“[abcdef][[x][yzw]]”是平衡的,因为左右权重都是偶数。

"[[abcde][xyzw]][Z]" 是平衡的,即使子字符串 "[abcde][xyzw]" 不是均衡!因为它的权重是 9,而“[Z]”的权重是 1,它们都是 ODD。

所以,我必须用 C 编写一个递归函数,它接收一个“字符串”。

int verify_weight(char s[])

    //Code I need here

它检查字符串和其中的子字符串,然后打印每个字符串是否平衡。 例如: 字符串“[[aa][b]][[[x][yy]][hhhhh]]”。 它打印这个: 不平衡:2,1 不平衡:1,2 平衡:3,5 不平衡:3,8

我还可以创建另一个函数来帮助解决它(仅限递归)。

编辑:(ANSWER) 谢谢你们的好解决方案,这是@kolmar 的解决方案。

代码:(在@kolmar 对我的函数名称的回答之后编辑)

#include "stdio.h"

int between_balanced(char s[], int n)

  if (!s[0] || (s[0] == ']' && n == 1)) return 0;
  return 1 + between_balanced(s+1, n + (
    s[0] == '[' ? 1 :
    s[0] == ']' ? -1 :
    0
  ));


int verify_weight(char s[])

  if (s[0] == '[') 
    int left = verify_weight(s+1);
    int right = verify_weight(s + between_balanced(s, 0) + 2);

    if (left % 2 == right % 2) 
      printf("balanced: ");
     else 
      printf("imbalanced: ");
    
    printf("%d,%d\n", left, right);

    return left+right;
   else 
    return between_balanced(s, 1);
  

int main() 
    char s[100];
    scanf("%s", s);
        printf("%d\n", verify_weight(s));
return 0;

很抱歉这个问题很长,但我真的需要帮助,我花了很多时间试图解决它,但我做不到。感谢您的时间和帮助!

【问题讨论】:

这是给我姐姐的,她有一个递归考试,她已经学习了几天。她问我这个问题,我无法解决,我们花了很多时间试图解决它,但我们不能......我认为我们在递归中遗漏了一些基本的东西。 你有没有尝试写任何东西? 是的,我确实尝试过,我不太擅长 C,但我也在 C# 中尝试过。它不适合我,我找不到如何到达右侧并检查它是否与左侧相同(奇数或偶数).. 基本函数应该找到字符串的左右两半,然后递归调用自己,得到两半的权重。然后它可以打印出它们是否平衡,并通过将两个递归调用的返回相加来返回总权重。您必须弄清楚如何处理基本情况,例如空字符串或没有子字符串的字符串。在您实现一些实际代码并返回您遇到的具体问题之前,我们真的无法为您做更多的事情。 在递归内部我猜想必须使用循环,“NO LOOPS”实际上是什么意思:不是迭代解决方案或根本没有循环? 【参考方案1】:

纯递归版本(使用辅助函数)。

这个想法是找到左边结束的地方(使用left_side),然后计算每边非括号字符的数量(使用count_side):

#include <stdio.h>
#include <string.h>

int left_side(char* s, int i, int depth) 
    if (depth == 0) return i;
    if (s[i] == '[') return left_side(s, i+1, depth+1);
    if (s[i] == ']') return left_side(s, i+1, depth-1);
    return left_side(s, i+1, depth);


int count_side(char* s, int a, int b) 
    if (a==b) return 0;
    return count_side(s, a+1, b) + (s[a] != '[' && s[a] != ']');


int is_balanced(char* s) 
    if (s[0] != '[') return 1;
    int size = strlen(s);
    int left = left_side(s, 1, 1);
    return count_side(s, 0, left)%2 == count_side(s, left, size)%2;


int main() 
    char s[256];
    while(scanf("%s", s) != EOF) 
        printf("%s\n", is_balanced(s) ? "YES" : "NO");
    

【讨论】:

OP 没有要求验证输入是否符合规范。只有在给定规范的情况下,它才是平衡的。并且 "ab[c]" 不是有效的输入字符串。参考文献“字符串始终是这两种格式之一”。 @BukLau 你能验证字符串是否始终有效吗? 我解释错了,只能是第一种格式,或者第二种格式。如果是第二种格式,则包含第一种格式,必须以“[”开头,以“]”结尾。对此感到抱歉..我也在问题中对其进行了编辑。 为了严格正确,您需要递归定义strlen ;-)【参考方案2】:

单功能解决方案:

// rs - if we are in the right side bracket now
// ob - unmatched open brackets in the left hand side
// wl - weight of left side
// wr - weight of right side
// call as check(your_string, 0, 0, 0, 0)
int check(char *s, int rs, int ob, int wl, int wr) 

  if (!*s) return !rs || wl % 2 == wr % 2;
  if (rs) return check(s+1, rs, ob, wl, wr+1);
  if (s[0] == ']') return check(s+1, ob==1, ob-1, wl, wr);
  if (s[0] == '[') return check(s+1, rs, ob+1, wl, wr);
  return check(s+1, rs, ob, wl+1, wr);

编辑:

这里是解决方案,它打印格式 2 中的每个子字符串,无论它是平衡的还是不平衡的:

#include "stdio.h"

int get_length(char *s, int open_brackets)

  if (!*s || (s[0] == ']' && open_brackets == 1)) return 0;
  return 1 + get_length(s+1, open_brackets + (
    s[0] == '[' ? 1 :
    s[0] == ']' ? -1 :
    0
  ));


int get_weight(char *s)

  if (s[0] == '[') 
    int left = get_weight(s+1);
    int right = get_weight(s + get_length(s, 0) + 2);

    if (left % 2 == right % 2) 
      printf("balanced: ");
     else 
      printf("imbalanced: ");
    
    printf("%d, %d\n", left, right);

    return left+right;
   else 
    return get_length(s, 1);
  

【讨论】:

不错的解决方案。只有一件事,“abc”不应该是有效的吗? [0][[00][0]] 在 [[00][0]] 中有一个不平衡的右侧,但这会返回 true。 @rpattiso 有一个不平衡的一面不会使整个字符串不平衡。参考文献""[[abcde][xyzw]][Z]" 是平衡的,即使..." ([[00][0]] 不是有效输入,只有 [00][0] 是)。 @JuanLopes 是的,忘记那个案子了,现在修好了。 @JuanLopes 你如何完成“它检查字符串和其中的子字符串,然后打印每个字符串是否平衡。”【参考方案3】:

让我们暂时忘记我们不能使用循环的事实,并尝试考虑一种解决它的算法。我们需要做的是为给定字符串中的每个左括号找到匹配的右括号的位置。之后解决方案变得几乎是微不足道的:我们可以创建一个辅助函数,它接受一个 char 数组和两个索引:下界和上界,并执行以下操作(它是一个伪代码):

// low is inclusive, high is not.
int get_weight(char[] s, int low, int high) 
    if (low == high || s[low] is not a bracket) // The base case: a simple string.
        return high - low;
    int mid = get_matching_index(low); // An index of a matching bracket.
    // Solves this problem recursively for the left and the right substrings.
    int left_weight = get_weight(s, low + 1, mid);
    int right_weight = get_weight(s, mid + 2, high - 1);
    // Prints balanced/imbalanced depending on the left_weight and the right_weight.
    return left_weight + right_weight;

所以问题是如何找到匹配的括号对。如果我们可以使用循环,我们可以应用一个标准的基于堆栈的算法(从左到右遍历字符串,如果它是一个左括号则将一个位置推入堆栈,如果它是一个右括号则从堆栈中弹出顶部元素一)。即使我们不允许使用循环,我们也可以模拟它:

int i;
for (i = 0; i < n; i++) 
    // do something 

可以实现为

void iterate_recursively(int i, int n) 
    if (i < n) 
        // do something
        iterate_recursively(i + 1, n);
    


...

iterate_recursively(0, n)

堆栈的实现不需要任何循环,就是这样。

【讨论】:

一定有办法解决,只用递归,不用栈(这个问题是给姐姐的,他们还没学过栈和队列) 我不知道问题的具体内容是什么,但通常当有人说编写递归解决方案时,这并不意味着您不能使用循环它只是意味着递归解决方案不是迭代的。如果您不想要循环,那么 ILoveCoding 建议了该方法。 我明白,但这个问题只需要递归。是给姐姐测试的,而且只用了递归。 那么ILoveCoding的方法就是你想要的 @BukLau 好吧,堆栈大小受字符串长度的限制,因此只需一个数组即可实现。【参考方案4】:

我的解决方案是基于将累积分数(权重)与 target_score 匹配。评分函数是哈希函数的非常简化的版本。由于“Balanced”是 8 字节长,因此该值应该可以是 64 位长整数。

用python编写,但它的方式类似于C。

def wfunc(s):
    ret = 0
    for x in s:
        ret <<= 8
        ret |= ord(x)
    return ret

def find_balanced(buf, index, score, target_score):
    end = len(buf) == index
    score_hit = score == target_score
    if end:
        return score_hit
    c = buf[index]
    enclosed = c == '[' or c == ']'
    if enclosed:
        if score_hit:
            return True
        else:
            # Reset accumulated value
            return find_balanced(buf, index+1, 0, target_score)
    score <<= 8
    score |= ord(c)
    return find_balanced(buf, index+1, score, target_score)


find_balanced('[Orama][Koma][[exmp][Balanced]', 0, 0, wfunc('Balanced')) # True
find_balanced('[Orama][Koma][[exmp][Balanceded]', 0, 0, wfunc('Balanced')) # False
find_balanced('[Orama][Koma][[exmp][Balance]', 0, 0, wfunc('Balanced')) # False

【讨论】:

以上是关于检查字符串是不是“平衡”的递归函数的主要内容,如果未能解决你的问题,请参考以下文章

利用递归函数实现检查一个字符串是否为回文数

将字符串与通配符模式匹配的递归函数

[模板]洛谷T3369 普通平衡树 链表&递归版无父指针版Splay

不使用向量、大小或其他参数的递归回文检查

递归的调用

C语言:编写一个测试一个串是不是为回文的递归函数,是回文,返回1;不是,返回0。