所涉及的递归调用到底发生了啥?

Posted

技术标签:

【中文标题】所涉及的递归调用到底发生了啥?【英文标题】:What exactly is happening in the recursive calls involved?所涉及的递归调用到底发生了什么? 【发布时间】:2015-11-06 12:10:35 【问题描述】:

所以,我试图解决以下问题:http://www.spoj.com/problems/MREPLBRC/en/

正则括号序列是一串仅由左括号和右括号组成的字符串,并且满足以下条件:

• 空字符串是常规括号序列。

• 如果 A 是正则括号 0 序列,则 (A)、[A] 和 A 也是正则括号序列。

• 如果 A 和 B 是正则括号序列,则 AB 也是正则括号序列。

例如,序列 [()]、 i [] 是正则序列,但序列 [(([, 和 [])([]不是。

Ivica 找到了一个看起来可能是常规括号序列的字符串。一些字符已经被弄脏并且难以辨认,并且可能是任何字符。

编写一个程序,计算字符串中难以辨认的字符有多少种方式可以被括号替换,从而得到一个规则的括号序列。这个数字可能很大,所以只输出它的最后 5 位。

输入

第一行包含一个偶数 N (2

第二行包含字符串。难以辨认的字符由“?”表示字符。

输出

输出字符串可以读取的常规括号序列的数量。

经过深思熟虑,我无法真正为此形成解决方案,因此我试图寻找解决方案。我在这里找到它:http://ruhinraihan.blogspot.in/2012/08/spoj-4038-counting-way-of-bracket.html

下面是代码:

#include<iostream>
#include<list>
#include<string>
#include<cstring>
#include<sstream>
#include<cctype>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<stack>
#include<fstream>
#include<cstdlib>
#include<vector>
#include<map>
#include<utility>
#include<iomanip>
#include<queue>

using namespace std;

#define INF (1<<29)
#define SET(a) memset(a,-1,sizeof(a))
#define ALL(a) a.begin(),a.end()
#define CLR(a) memset(a,0,sizeof(a))
#define FILL(a,v) memset(a,v,sizeof(a))
#define PB push_back
#define FOR(i,n) for(int i = 0;i<n;i++)
#define PI acos(-1.0)
#define EPS 1e-9
#define MP(a,b) make_pair(a,b)
#define READ(f) freopen(f, "r", stdin)
#define WRITE(f) freopen(f, "w", stdout)
#define LL long long
#define MOD 100000

bool moduloUsed;
LL memo[200+10][200+10];
string s;

LL func(int left, int right)

    int i, valid;
    if(left>right)  return 1;

    if(memo[left][right]!=-1)   return memo[left][right];

    LL ret=0;

    for(i=left+1;i<=right;i+=2)
    
        if(s[left]=='(' && s[i]==')') valid=1;
        else if(s[left]=='' && s[i]=='') valid=1;
        else if(s[left]=='[' && s[i]==']') valid=1;
        else if(s[left]=='?' && s[i]==')') valid=1;
        else if(s[left]=='?' && s[i]=='') valid=1;
        else if(s[left]=='?' && s[i]==']') valid=1;
        else if(s[left]=='(' && s[i]=='?') valid=1;
        else if(s[left]=='' && s[i]=='?') valid=1;
        else if(s[left]=='[' && s[i]=='?') valid=1;
        else if(s[left]=='?' && s[i]=='?') valid=3;
        else    valid=0;

        ret+=valid*func(left+1,i-1)*func(i+1,right);

        if(ret>MOD)
        
            moduloUsed=true;
            ret%=MOD;
        
    
return memo[left][right]=ret;



int main()

    LL ans,length;

    while(cin>>length>>s)
    
        SET(memo);
        ans=func(0,length-1);
        if(!moduloUsed)
        cout<<ans<<endl;
        else
        printf("%05lld\n",ans);
    

return 0;

我想知道,函数fun 中到底发生了什么。我知道它正在遍历输入字符串,并检查平衡括号条件的可能组合,但是,func(left+1,i-1)*func(i+1,right),这两个递归调用代表什么?请帮我。

【问题讨论】:

但是您是否也知道在 C 和 C++ 中有 switch/case 语句? @decltype_auto,你为什么在这里询问开关/案例?可以用另一种方式重写这段代码(使用switch),但这无助于Harry 理解这段代码的逻辑。 @decltype_auto,无意冒犯,但如果您停止向其他人炫耀您的知识渊博并帮助我,这对我会有很大帮助。正是因为像你这样的人,很多初学者常常因为你试图表现出你惊人的幽默而无法学习,相信我,这根本没有那么神奇。 @decltype_auto,我愿意,但是,与其说对任何人都没有帮助的话,不如让你的答案(这有助于 OP)说出我的感受。不要说它会提高可读性,而是写一个答案来表明它会提高可读性。如果是这样,我很乐意接受您的回答。 @decltype_auto,天哪。这太神奇了。您自己建议代码需要重组,现在您自己说 SO 不是代码编写服务。至少看看我问的问题。我从来没有要求你为我写代码。我只是请你帮我解释一下逻辑。至少在一件事上保持头脑清醒。 【参考方案1】:

基本上,答案是通过将任何开括号对的所有有效组合相乘来得出的。冗长的 if 语句是递归函数的停止条件,因为任何对都可以有 0,1 或 3 个有效组合。让你失望的递归调用实际上是(结合 for 循环)扫描整个字符串以查找括号对。如果说字符串是对称的,那将是一个更简单的调用:fun(left + 1, right -1)

【讨论】:

func(left+1,i-1)具体代表什么?我认为 func(i+1,right) 基本上意味着,我们正在为下一个位置做这件事。另外,为什么我们将 i 增加 2 个单位?为什么不只有 1 个? func(left+1, i-1) 是从左边开始的子串,另一个是从右边结束的子串。它们仅在整个字符串中重叠 实际上,我认为更好的措辞是 that one Treats ? chars 作为左括号,另一个作为右括号。希望我没有记错。

以上是关于所涉及的递归调用到底发生了啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何使涉及期货尾递归的函数?

递归过程中的第一个过程调用发生堆栈溢出

java中方法的递归调用

Js arguments.callee();函数自己调用自己

Java中的递归详解

在递归函数参数中修改列表时会发生啥