C++信奥之径,锻炼思维,扎实算法——递推与递归(4)

C++信奥之径,锻炼思维,扎实算法——递推与递归(4)

编码文章call10242025-05-03 12:33:573A+A-

外星密码

题目描述

算法解析

首先明确输入的字符只有数字、大写字母、"["和"]",因此直接对4种字符进行判断即可。遇到 "["说明与匹配的"]"中间字符串需要解压缩。

这里有三种解决的思路:


1、利用字符串函数模拟

根据题意,我们需要首先找到最内侧的一对括号,对里面的字符串进行解压。

为了找到最后一个左括号"[",需要从后往前遍历字符串:

for(int i=s.size()-1;i>=0;i--){
    if(s[i]=="["){
        left=i;
        break;
    }
}

再从left开始向右寻找,一定能找到与之匹配的右括号:

for(int i=left+1;i<=s.size()-1;i++){
    if(s[i]=="]"){
        right=i;
        break;
    }
}

接下来取出两个括号的中间部分,并进行解压。取出中间部分可以使用substr函数:

string zistr = substr(s,left+1,right-left-1); //zistr是子字符串


对其进行处理后,再将从左括号到右括号的所有字符替换掉,需要用到字符串的replace函数

s.replace(left,right-left+1,fun(zistr));


最后就需要实现fun()函数的功能:解压缩子字符串,并返回解压后的字符串。

在fun()函数中,可以使用isdigit()找出子串中的数字cnt,isalpha()找出子串中的字符串。其他的字符判断函数,读者们可以点击下方文章了解:

C++信奥之径,锻炼思维,扎实算法——模拟与高精度算法(8)

判断出来后,将子串中的字母组成的字符串循环cnt遍,返回该结果即可。具体的fun()函数如下:

string fun(string s){
    int cnt=0,i=0;
    while(isdigit(s[i])){
        cnt=cnt*10+s[i]-'0';//将数字字符拼接成真正的数字
        i++;
    }
    string x="",y=""; 
    for( ;i<s.size();i++){
        x+=s[i];
    }
    while(cnt--)y+=x;
    return y;
}

完整的代码,还请读者们理解思路后自行编写,这里就不展示了。


2、使用栈进行模拟

由于程序运行与括号有关,想到可以使用栈结构来存储数据。

在读入整个字符串后,一个一个字符进行判断,遇到"["和大写字母,全都压入栈,当遇到"]"时进行字符串处理:

(1)先弹出前几位字母,还原成原本的子串X;

(2)再弹出前几位数字,还原成原本的正数D;

(3)注意多弹出一个"[";

(4)为了类型统一,解压后需要将字符串一个一个字符压入栈;

这里要特别注意,出栈顺序与原顺序相比是倒序的,在拼接字符串和数字时要注意转换。

由于题目中限定解压后的字符串长度不超过20000,因此可以采用栈来存放。如果长度过长,这个方法会导致栈溢出。

代码展示:

#include<iostream>
#include<stack>
#include<string>
using namespace std;
stack<char> st;
string ans="",x=""; //x临时存放括号中的字符串
string d=""; //d临时存放压缩的数字
int main(){
    string s;
    cin>>s;
    for(int i=0;i<s.size();i++){
        if(s[i]==']'){
            x=""; //x先清空
            while(st.top()>='A'){
                x=st.top()+x; //注意倒序的拼接
                st.pop();
            }
            //cout<<x<<endl;
            d=""; //临时数字先清空
            while(st.top()!='['){
                d=st.top()+d; //同样注意倒序
                st.pop();
            }
            int n=stoi(d); //使用stoi()函数直接字符串转数字 
            //cout<<d<<endl;
            st.pop(); //记得多弹出一个'[' 
            for(int j=0;j<n;j++){
                for(int k=0;k<x.size();k++){
                    st.push(x[k]); //要一个一个字符再压回去
                }
            }
        }
        else st.push(s[i]);
    }
    while(!st.empty()){ //最后栈里所有元素拼成答案 
        ans=st.top()+ans;
        st.pop();
    } 
    cout<<ans;
    return 0;
}


(3)使用递归思想

考虑到最终结果是纯大写字母的字符串,不妨分析一下字母会出现在哪:"[]"的外面和里面,其中,在"[]"里面的字符串,又分为外面的字符串和内部"[]"中的字符串。

因此我们可以将问题分解成各个子任务:"[]"外部的字符串原样组合,"[]"内部的字符串进行解压。递推公式可以表示为:

f("A[DX]")="A"+"D*f("X")(其中,A为[]外字符串,D为正数,X为内部压缩的字符串)

边界条件为:f(A)=A,即没有括号的话,就原样输出。

在代码实现时,可以充分利用cin的特点,一边输入一边递归,最终递归函数的返回值就是答案。

代码展示:

#include<iostream>
using namespace std;
string fun(){
    int num;
    char ch;
    string s="",tmp="";
    while(cin>>ch){
        if(ch=='['){ //如果有 '[',准备递归 
            cin>>num; //利用cin的缓冲机制直接读入数字 
            tmp=fun(); //递归处理后续的字符串 
            while(num--){
                s+=tmp; //解压:字符串复制num次 
            }
        }
        else if(ch==']'){ //到']',解压完毕,返回[]中解压的结果 
            return s;
        }
        else{
            s+=ch; //[]外面的字符,直接累加进答案 
        }
    }
    return s;
}
int main(){
    string ans=fun();
    cout<<ans<<endl;
    return 0;
}


运行结果

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4