Tag Archives: 递归

LeetCode Generate Parentheses

22. Generate Parentheses

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

要求产生所有包含n对括号的合法字符串。 这一看就是递归的题,我开始用DFS来解,假设□表示所有n-1对括号的合法字符串,则要产生所有n对括号的合法字符串,有三种可能的情况,可以把新的括号对加在原有字符串的前面,后面或者包住之前的字符串,即”()□”、”□()”和”(□)”。但是有一个问题,即如果□是”()()()()”这样的形式,则新增的括号放在前面和后面是同一种,所以需要判断□是否对称。 第一版代码如下:

typedef struct Par_Pair {
    string par;
    bool is_symmetric;
};
class Solution {
public:
    void dfs(int step, vector<Par_Pair>& ans)
    {
        if (step == 1) {
            Par_Pair pp;
            pp.par = "()";
            pp.is_symmetric = true;
            ans.push_back(pp);
            return;
        }
        dfs(step – 1, ans);
        int sz = ans.size();
        for (int i = 0; i < sz; i++) {
            if (ans[i].is_symmetric) {
                Par_Pair pp;
                pp.is_symmetric = false;
                pp.par = "(" + ans[i].par + ")";
                ans.push_back(pp);
                ans[i].par = "()" + ans[i].par;
            }
            else {
                Par_Pair pp;
                pp.is_symmetric = false;
                pp.par = "()" + ans[i].par;
                ans.push_back(pp);
                pp.par = ans[i].par + "()";
                ans.push_back(pp);
                ans[i].par = "(" + ans[i].par + ")";
            }
        }
    }
    vector<string> generateParenthesis(int n)
    {
        vector<Par_Pair> ans;
        dfs(n, ans);
        vector<string> rs;
        for (int i = 0; i < ans.size(); i++)
            rs.push_back(ans[i].par);
        return rs;
    }
};


本代码提交WA,当n=4时,会漏掉”(())(())”这种情况,这种情况是两个”(())”;同时会重复”()(())()”这种情况,因为n=3时”()(())”和”(())()”是不同合法字符串,且不对称,当n=4时,增加一对括号,可以在前或后,导致出现重复。

看网上题解,用递归方法,真是漂亮。不是考虑同时增加一对括号,而是左右括号分别考虑。在放置括号时,为了合法,总是使剩余的左括号数少于右括号数,且当左右括号数相等时,先放置左括号。

其实自己枚举下剩余左右括号的数目left和right的大小关系,只有<、=、>三种关系,考虑下每种关系合法的放置操作即可。

class Solution {
public:
    void recur_gen(int left, int right, string s, vector<string>& ans)
    {
        if (left == 0 && right == 0) {
            ans.push_back(s);
            return;
        }
        if (left > 0)
            recur_gen(left – 1, right, s + "(", ans);
        if (right > 0 && left < right)
            recur_gen(left, right – 1, s + ")", ans);
    }
    vector<string> generateParenthesis(int n)
    {
        vector<string> ans;
        string s = "";
        recur_gen(n, n, s, ans);
        return ans;
    }
};

本代码提交AC,用时0MS。

LeetCode Integer to Roman

12. Integer to Roman

Roman numerals are represented by seven different symbols: IVXLCD and M.

Symbol       Value
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

For example, two is written as II in Roman numeral, just two one’s added together. Twelve is written as, XII, which is simply X + II. The number twenty seven is written as XXVII, which is XX + V + II.

Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used:

  • I can be placed before V (5) and X (10) to make 4 and 9. 
  • X can be placed before L (50) and C (100) to make 40 and 90. 
  • C can be placed before D (500) and M (1000) to make 400 and 900.

Given an integer, convert it to a roman numeral. Input is guaranteed to be within the range from 1 to 3999.

Example 1:

Input: 3
Output: "III"

Example 2:

Input: 4
Output: "IV"

Example 3:

Input: 9
Output: "IX"

Example 4:

Input: 58
Output: "LVIII"
Explanation: L = 50, V = 5, III = 3.

Example 5:

Input: 1994
Output: "MCMXCIV"
Explanation: M = 1000, CM = 900, XC = 90 and IV = 4.

本题要求把一个阿拉伯数字转换为罗马数字。 首先要了解罗马数字的表示形式,具体看Roman numerals WIKI。 罗马数字由上左图的7个基本字母组成,直接递归很容易求解。但是会有一些特殊情况,比如4,如果递归求解就是IIII,为了简便,采用5-1也就是IV的形式。上面右图就是基本的特殊情况。考虑特殊情况之后的递归代码如下:

class Solution {
public:
    string work(int num, string cur)
    {
        if (num >= 1000)
            return work(num – 1000, cur + "M");
        if (num >= 500) {
            if (num >= 900)
                return work(num – 900, cur + "CM");
            return work(num – 500, cur + "D");
        }
        if (num >= 100) {
            if (num >= 400)
                return work(num – 400, cur + "CD");
            return work(num – 100, cur + "C");
        }
        if (num >= 50) {
            if (num >= 90)
                return work(num – 90, cur + "XC");
            return work(num – 50, cur + "L");
        }
        if (num >= 10) {
            if (num >= 40)
                return work(num – 40, cur + "XL");
            return work(num – 10, cur + "X");
        }
        if (num >= 5) {
            if (num >= 9)
                return work(num – 9, cur + "IX");
            return work(num – 5, cur + "V");
        }
        if (num < 5) {
            if (num >= 4)
                return work(num – 4, cur + "IV");
            while (num > 0) {
                cur += "I";
                num–;
            }
            return cur;
        }
    }
    string intToRoman(int num) { return work(num, ""); }
};

本代码提交AC,用时32MS,这么多if语句,真是不够优雅。后来看到网上一种解法,简洁漂亮许多,贴出来学习一下:

class Solution {
public:
    string intToRoman(int num)
    {
        vector<int> nums = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
        vector<string> symbol = { "I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M" };
        string ans = "";
        for (int i = nums.size() – 1; i >= 0; i--) {
            while (num >= nums[i]) {
                ans += symbol[i];
                num -= nums[i];
            }
        }
        return ans;
    }
};

本代码提交AC,用时56MS,比上一版稍慢,不够这版更好看。

hihoCoder 1039-字符消除

hihoCoder 1039-字符消除 #1039 : 字符消除 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi最近在玩一个字符消除游戏。给定一个只包含大写字母”ABC”的字符串s,消除过程是如下进行的: 1)如果s包含长度超过1的由相同字母组成的子串,那么这些子串会被同时消除,余下的子串拼成新的字符串。例如”ABCCBCCCAA”中”CC”,”CCC”和”AA”会被同时消除,余下”AB”和”B”拼成新的字符串”ABB”。 2)上述消除会反复一轮一轮进行,直到新的字符串不包含相邻的相同字符为止。例如”ABCCBCCCAA”经过一轮消除得到”ABB”,再经过一轮消除得到”A” 游戏中的每一关小Hi都会面对一个字符串s。在消除开始前小Hi有机会在s中任意位置(第一个字符之前、最后一个字符之后以及相邻两个字符之间)插入任意一个字符(‘A’,’B’或者’C’),得到字符串t。t经过一系列消除后,小Hi的得分是消除掉的字符的总数。 请帮助小Hi计算要如何插入字符,才能获得最高得分。 输入 输入第一行是一个整数T(1<=T<=100),代表测试数据的数量。 之后T行每行一个由’A”B”C’组成的字符串s,长度不超过100。 输出 对于每一行输入的字符串,输出小Hi最高能得到的分数。 提示 第一组数据:在”ABCBCCCAA”的第2个字符后插入’C’得到”ABCCBCCCAA”,消除后得到”A”,总共消除9个字符(包括插入的’C’)。 第二组数据:”AAA”插入’A’得到”AAAA”,消除后得到””,总共消除4个字符。 第三组数据:无论是插入字符后得到”AABC”,”ABBC”还是”ABCC”都最多消除2个字符。 样例输入 3 ABCBCCCAA AAA ABC 样例输出 9 4 2


这个题目要稍微思考一下,当然很容易想到穷举测试,但我在边写代码的时候还是在担心穷举会不会超时什么的。后来结果还不错。 总体思路就是在字符串的所有可以插空的位置分别插入A、B、C看能消除多少个字符,然后求最大值。所以这里关键就是怎样求一个字符串最终能消除多少个字符。 题目给出的消除策略是这样的:
1)如果s包含长度超过1的由相同字母组成的子串,那么这些子串会被同时消除,余下的子串拼成新的字符串。 2)上述消除会反复一轮一轮进行,直到新的字符串不包含相邻的相同字符为止。
一定要注意,相同的子串同时消除。我一开始想用堆栈来做,来一个字符,判断是否和栈顶相同,如果相同,则消除,并统计消除个数。但是这样会有问题,比如字符串ABCCBCCCAA,如果用堆栈来做的话:进A,进B,进C,C,消除C,C,进B,消除B,B,进C,C,C,消除C,C,C,进A,A,消除A,A,A,这样所有字符都消除了。但是按原题的思路,所有相同的子串同时消除,第一轮同时只能消除C,C,C,C,C,A,A,得到ABB,然后消除B,B,最终剩A。所以用堆栈不行。 后来是这样解决的,只需遍历一遍字符串,如果s[i]==s[i+1]则说明出现重复子串了,令j=i+1,然后一个while(s[i]==s[j])循环计数。如果可以消除,则递归调用该函数继续消除,否则直接返回。 代码如下: [cpp] #include <iostream> #include<string> using namespace std; //递归求解一个字符串最多能消除多少个字符 int get_erase_num(string s,int rs) { int G[100]={0};//用来记录整个字符串能够消除的字符的位置 int s_size=s.size(); bool is_done=true;//是否检查结束的标识 for(int i=0;i<s_size-1;i++)//注意上限s_size-1 { if(s[i]==s[i+1])//然后判断是否有至少两个连续的字符出现 { G[i]=1; is_done=false; rs++; int j=i+1; while(s[i]==s[j]) { G[j]=1; rs++; j++; if(j>=s_size)//检查是否超限 { break; } } i=j-1; } } if(!is_done)//如果可以消除,则还没有结束 { string ss=""; for(int i=0;i<s_size;i++) { if(G[i]==0) ss+=s[i];//同时消除重复子串后的结果串 } return get_erase_num(ss,rs);//递归 } return rs;//结束返回 } int main() { int n; string s; cin>>n; while(n–) { cin>>s; int s_size=s.size(); int max_m=-1; //在所有可以插入的位置依次分别测试插入A,B,C的结果 for(int i=0;i<s_size+1;i++) { if(i==s_size)//如果要在字符串的末尾插入字符 { int A_result=get_erase_num(s+"A",0); if(A_result>max_m) max_m=A_result; int B_result=get_erase_num(s+"B",0); if(B_result>max_m) max_m=B_result; int C_result=get_erase_num(s+"C",0); if(C_result>max_m) max_m=C_result; } else//插入位置除了末尾 { int A_result=get_erase_num(s.substr(0,i)+"A"+s.substr(i),0); if(A_result>max_m) max_m=A_result; int B_result=get_erase_num(s.substr(0,i)+"B"+s.substr(i),0); if(B_result>max_m) max_m=B_result; int C_result=get_erase_num(s.substr(0,i)+"C"+s.substr(i),0); if(C_result>max_m) max_m=C_result; } } cout<<max_m<<endl; } return 0; } [/cpp] 该代码提交结果AC,运行用时45ms,内存0MB,还不错。]]>