LeetCode 301. 删除无效的括号

思路

本题有两点需要解决,如果确定括号的最少删除数量以及删除哪些括号

求出最少删除数量

思路见 LeetCode 22. 括号生成

已知删除数量,删除哪些括号?

直接爆搜,枚举每个括号是否删除

  • 如果当前是左括号,枚举是否删除 (删除 0 还是 1 个)
  • 如果当前是左括号,枚举是否删除 (删除 0 还是 1 个)
  • 如果不是括号,不能删除,直接递归下一步

假设输入为 ())))),爆搜出来的答案会重复,耗时。为此,遇到连续多个括号时,不应该枚举哪个括号应该删除,而应该枚举的数量,我们人为规定删除顺序从左到右,这是第一处剪枝

直接枚举括号是否删除,那么如何保证括号序列合法呢?

  • 方法 1:加入答案之前,使用 check 函数判断是否合法,合法再加入,可以过,但时间复杂度增加
bool check(string s)
{
	int cnt = 0,n = s.length();
	for(int i = 0 ; i < n ; i ++)
	{
		if(s[i] =='(') cnt ++;
		else if(s[i] == ')') cnt --;
		if(cnt < 0) return false;
	}
	return cnt == 0;
}
  • 本题用的方法 2:枚举过程中,时刻保证左括号数量>右括号数量,为此需要维护一个 cnt 变量,记录左括号和右括号数量的差值
    • 刚进入递归时,path 是满足 cnt>0
    • 如果当前字符不是括号,加入 path,继续递归
    • 如果当前字符是括号,倒序枚举删除多少括号 (删除所有括号~删除 0 个括号),反过来理解就是,正序枚举 path 添加多少括号
    • path 添加左括号后一定满足条件,而添加右括号后可能非法了,因此需要保证 cnt>0 的情况下,添加右括号进入下一步递归,这是第二处剪枝

时间复杂度

最坏情况下每个位置有两种选择,去掉和不去掉,最后需要 O (n)的时间拷贝答案,故时间复杂度为 O ($n2^{n}$)

方法1代码

class Solution {
public:
    vector<string> ans;
    vector<string> removeInvalidParentheses(string s) {
        //lr分别记录要删除的左右括号数量
        int l=0,r=0;
        for(auto c:s)
            if(c=='(')  l++;
            else if(c==')')
            {
                if(l==0)    r++;
                else l--;
            }
        dfs(s,"",0,l,r);
        return ans;
    }
    bool check(string s)
    {
        int cnt = 0,n = s.length();
        for(int i = 0 ; i < n ; i ++)
        {
            if(s[i] =='(') cnt ++;
            else if(s[i] == ')') cnt --;
            if(cnt < 0) return false;
        }
        return cnt == 0;
    }
    void dfs(string s,string path,int u,int l,int r)//枚举删除哪些括号
    {
        int n=s.size();
        if(u>=n)
        {
            if(check(path))
                ans.push_back(path);
            return;
        }
        if(s[u]=='(')
        {
            //找出连续括号数量
            int t=u;
            while(t<n&&s[t]=='(')   t++;
            //倒序枚举删除的数量
            l-=t-u;
            for(int i=t-u;i>=0;i--)
            {
                if(l>=0)
                    dfs(s,path,t,l,r);
                path=path+'(';
                l++;
            }
        }
        else if(s[u]==')')
        {
            //找出连续括号数量
            int t=u;
            while(t<n&&s[t]==')')   t++;
            //倒序枚举删除的数量
            r-=t-u;
            for(int i=t-u;i>=0;i--)
            {
                if(r>=0)
                    dfs(s,path,t,l,r);
                path=path+')';
                r++;
            }
        }
        else//当前不是括号,直接递归下一步    
            dfs(s,path+s[u],u+1,l,r);
    }
};

方法2代码

class Solution {
public:
    vector<string> ans;
    vector<string> removeInvalidParentheses(string s) {
        //lr分别记录要删除的左右括号数量
        int l=0,r=0;
        for(auto c:s)
            if(c=='(')  l++;
            else if(c==')')
            {
                if(l==0)    r++;
                else l--;
            }
        dfs(s,"",0,l,r,0);
        return ans;
    }
    
    void dfs(string s,string path,int u,int l,int r,int cnt)//枚举删除哪些括号
    {
        int n=s.size();
        if(u>=n)
        {
            if(!cnt)
                ans.push_back(path);
            return;
        }
        if(s[u]=='(')
        {
            //找出连续括号数量
            int t=u;
            while(t<n&&s[t]=='(')   t++;
            //倒序枚举删除的数量
            l-=t-u;
            for(int i=t-u;i>=0;i--)
            {
                if(l>=0)
                    dfs(s,path,t,l,r,cnt);
                path=path+'(';
                l++;cnt++;
            }
        }
        else if(s[u]==')')
        {
            //找出连续括号数量
            int t=u;
            while(t<n&&s[t]==')')   t++;
            //倒序枚举删除的数量
            r-=t-u;
            for(int i=t-u;i>=0;i--)
            {
                if(r>=0&&cnt>=0)
                    dfs(s,path,t,l,r,cnt);
                path=path+')';
                r++;cnt--;
            }
        }
        else//当前不是括号,直接递归下一步    
            dfs(s,path+s[u],u+1,l,r,cnt);
    }
};
posted @   穿过雾的阴霾  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
历史上的今天:
2022-07-18 css设置样式常用参数
点击右上角即可分享
微信分享提示