【字符串】【括号匹配】【广度优先】301. 删除无效的括号

简介: 【字符串】【括号匹配】【广度优先】301. 删除无效的括号

本文涉及知识点

字符串 括号匹配 广度优先

LeetCode301 删除无效的括号

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。

返回所有可能的结果。答案可以按 任意顺序 返回。

示例 1:

输入:s = “()())()”

输出:[“(())()”,“()()()”]

示例 2:

输入:s = “(a)())()”

输出:[“(a())()”,“(a)()()”]

示例 3:

输入:s = “)(”

输出:[“”]

提示:

1 <= s.length <= 25

s 由小写英文字母以及括号 ‘(’ 和 ‘)’ 组成

s 中至多含 20 个括号

预备知识

合法括号的定义:

一,()是合法括号。

二,A和B是合法括号。则AB也是合法括号。A=() B = () 则()()是合法括号。

三,(A)是合法括号。A=()(),则(()())是合法括号。

性质:

num=0,从左到右解析括号,遇到(++,遇到)–。性质一:num任何时候都不能为负。性质二:num最终必须为0。

定义是性质的充分条件容易证明,下面来证明必要条件。

令num1[i]是s[0,i]的num之和。

image.png

num1[j]不为0且大于等于0 → \rightarrow num1[j]大于等于1

移除(后,nums1[j]全部-- 任然符合性质一

s[i]合法→ \rightarrow num1[i]==0 → \rightarrow num1[i-1]为1

移除(后,变成0,符合性质二。

不断拆分,知道s的长度为2为止。

广度优先

初始字符为空。

hasAdd 记录增加了多少字符。

num 记录字符的权值和,(为1,)为-1。

image.png

删除)时,无需判断,因为只会让其它)的num变大或不变。

删除)时,需要判断,因为可能让某个)的num变成负数。

代码

核心代码

class Solution {
public:
  vector<string> removeInvalidParentheses(string s) {
    int num = 0;
    unordered_set<string> pre = { "" };
    int iDelLeft=0,iDelRight = 0;
    for (int i = 0; ; i++)
    {
      unordered_set<string> dp;
      const int hasAdd = i - iDelLeft -iDelRight;
      if (num < 0)
      {//删除一个右括号
        iDelRight++;
        num++;
        for (const string& str : pre)
        {
          for (int j = 0; j < str.length(); j++)
          {
            if (')' == str[j])
            {
              dp.emplace(str.substr(0, j) + str.substr(j + 1));
            }
          }
        }
      }
      else if (hasAdd < s.length())
      {
        if ('(' == s[hasAdd])
        {
          num++;
        }
        else if (')' == s[hasAdd])
        {
          num--;
        }
        for (const string& str : pre)
        {
          dp.emplace(str + s[hasAdd]);
        }
      }
      else 
      {
        if (num > 0)
        {//删除一个左括号
          num--;
          iDelLeft++;
          for (const string& str : pre)
          {
            for (int j = 0; j < str.length(); j++)
            {
              if ('(' == str[j])
              {
                dp.emplace(str.substr(0, j) + str.substr(j + 1));
              }
            }
          }
        }
        else
        {
          break;
        }
      }
      dp.swap(pre);
    }
    vector<string> vRet;
    for (const string& str : pre)
    {
      if (IsVilid(str))
      {
        vRet.emplace_back(str);
      }
    }
    return vRet;
  }
  bool IsVilid(const string& str)
  {
    int num = 0;
    for (const auto& ch : str)
    {
      if ('(' == ch)
      {
        num++;
      }
      else if (')' == ch)
      {
        num--;
      }
      if (num < 0)
      {
        return false;
      }
    }
    return 0 == num;
  }
};

测试用例

template<class T,class T2>
void Assert(const T& t1, const T2& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  string s;
  {
    Solution sln;
    s = ")()(";
    auto res = sln.removeInvalidParentheses(s);
    Assert({ "()" }, res);
  }
  {
    Solution sln;
    s = ")(";
    auto res = sln.removeInvalidParentheses(s);
    Assert({ "" }, res);
  }
  {
    Solution sln;
    s = "()())()";
    auto res = sln.removeInvalidParentheses(s);
    Assert({ "(())()", "()()()" }, res);
  }
  
  {
    Solution sln;
    s = "(a)())()";
    auto res = sln.removeInvalidParentheses(s);
    Assert({ "(a())()","(a)()()" }, res);
  }
  
}

简洁版

class CBracket
{
public:
  static int ToInt(const char& ch)
  {
    if ('(' == ch)
    {
      return 1;
    }
    return (')' == ch) ? -1 : 0;
  }
  static bool IsVilid(const string& str)
  {
    int num = 0;
    for (const auto& ch : str)
    {
      num += ToInt(ch);
      if (num < 0)
      {
        return false;
      }
    }
    return 0 == num;
  }
};
class Solution {
public: 
  vector<string> removeInvalidParentheses(string s) {
    int num = 0;
    unordered_set<string> pre = { "" };
    int iHasAdd = 0;
    while(true)
    {
      unordered_set<string> dp; 
      auto Del = [&pre,&dp](char ch)
      {
        for (const string& str : pre)
        {
          for (int j = 0; j < str.length(); j++)
          {
            if (ch == str[j])
            {
              dp.emplace(str.substr(0, j) + str.substr(j + 1));
            }
          }
        }
      };
      if (num < 0)
      {//删除一个右括号
        num++;
        Del(')');
      }
      else if (iHasAdd < s.length())
      {
        num += CBracket::ToInt(s[iHasAdd]);
        for (const string& str : pre)
        {
          dp.emplace(str + s[iHasAdd]);
        }
        iHasAdd++;
      }
      else 
      {
        if (num > 0)
        {//删除一个左括号
          num--;  
          Del('(');
        }
        else
        {
          break;
        }
      }
      dp.swap(pre);
    }
    vector<string> vRet;
    for (const string& str : pre)
    {
      if (CBracket::IsVilid(str))
      {
        vRet.emplace_back(str);
      }
    }
    return vRet;
  }
  
};

2023年4月版

class Solution {
public:
vector removeInvalidParentheses(string s) {
int iNeedDelLeft = 0, iNeedDelRight = 0;
{
for (int i = 0; i < s.length(); i++)
{
const char& ch = s[i];
if ((‘(’ == ch) || (‘)’ == ch))
{
vRangeIndexs.emplace_back(i);
}
if (‘(’ == ch)
{
iNeedDelLeft++;
}
if (‘)’ == ch)
{
if (iNeedDelLeft > 0)
{
iNeedDelLeft–;
}
else
{
iNeedDelRight++;
}
}
}
}
m_iMaskNum = 1 << vRangeIndexs.size();
auto Is = [&](const int iMask)
{
int iNum = 0;
for (int j = 0; j < vRangeIndexs.size(); j++)
{
if (iMask & (1 << j))
{//此位置删除
continue;
}
const char& ch = s[vRangeIndexs[j]];
if (‘(’ == ch)
{
iNum++;
}
if (‘)’ == ch)
{
iNum–;
}
if (iNum < 0)
{
return false;
}
}
return 0 == iNum;
};
std::unordered_set setRet;
for (int iMask = 0; iMask < m_iMaskNum; iMask++)
{
if (bitcount(iMask) != (iNeedDelLeft + iNeedDelRight))
{
continue;
}
if (!Is(iMask))
{
continue;
}
std::vector indexs,subIndexs;
for (int i = 0; i < s.length(); i++)
{
indexs.emplace_back(i);
}
for (int j = 0; j < vRangeIndexs.size(); j++)
{
if (iMask & (1 << j))
{//此位置删除
subIndexs.emplace_back(vRangeIndexs[j]);
}
}
vector v;
string str;
std::set_difference(indexs.begin(), indexs.end(), subIndexs.begin(), subIndexs.end(), std::back_inserter(v));
for (const auto& tmp : v)
{
str += s[tmp];
}
setRet.emplace(str);
}
return vector(setRet.begin(), setRet.end());
}
int m_iMaskNum;
vector vRangeIndexs;
};


扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。

https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关文章
|
机器学习/深度学习 算法 Python
LightGBM中的特征选择与重要性评估
LightGBM中的特征选择与重要性评估【2月更文挑战第1天】
2636 0
|
8月前
|
应用服务中间件 nginx
Nginx进程配置指令详解
Nginx进程配置指令主要包括:`worker_processes`设置工作进程数;`worker_cpu_affinity`绑定CPU核心;`worker_rlimit_nofile`设置最大文件描述符数量;`worker_priority`设置进程优先级;`worker_connections`设置最大连接数;`daemon`控制守护进程模式;`master_process`启用主进程模式;`pid`设置PID文件路径;`user`指定用户和组;`error_log`配置错误日志。这些指令在`nginx.conf`中配置,用于优化和控制Nginx的运行行为。
390 10
|
11月前
|
API Python
PAI EAS Flask应用部署Quick Start
本文介绍了如何将Python Flask应用快速部署到阿里云PAI EAS,并通过API对外提供服务。示例代码包括`web.py`和`demo.py`两个文件,展示了基本的Flask应用和跨文件导入功能。最后,通过阿里云控制台完成服务部署和调用。
388 28
|
安全 数据安全/隐私保护 Python
2FA
【9月更文挑战第29天】
858 4
|
Java 开发者
如何通过易语言多线程提升程序响应速度?
如何通过易语言多线程提升程序响应速度?
|
NoSQL 程序员 Linux
轻踩一下就崩溃吗——踩内存案例分析
踩内存问题分析成本较高,尤其是低概率问题困难更大。本文详细分析并还原了两个由于动态库全局符号介入机制(it's a feature, not a bug)触发的踩内存案例。
|
机器学习/深度学习 自然语言处理 搜索推荐
构建智能搜索应用:Elasticsearch与自然语言处理的融合
【8月更文第28天】随着大数据和人工智能技术的发展,用户对搜索应用的需求已经从简单的关键词匹配转向了更加智能化、人性化的交互方式。本文将探讨如何利用Elasticsearch和自然语言处理(NLP)技术构建一个能够理解用户意图并提供精准搜索结果的智能搜索系统。
1097 0
|
存储 安全 Java
深入解析 Java 中的 Synchronized:原理、实现与性能优化
深入解析 Java 中的 Synchronized:原理、实现与性能优化
531 1