我最近接触到了这个有趣的问题。您将获得一个仅包含字符'('
,')'
,'{'
,'}'
,'['
和']'
的字符串,例如{{1} 1}},你需要编写一个函数来检查这样一个输入字符串的有效性,函数可能是这样的:
"[{()}]"
这些括号必须以正确的顺序关闭,例如bool isValid(char* s);
和"()"
都有效,但"()[]{}"
,"(]"
和"([)]"
不是!
我推出了O(n)时间和O(n)空间复杂度解决方案,效果很好:
"{{{{"
时,'('
或'{'
将其推入堆栈。'['
,')'
或'}'
时,检查堆栈顶部是否对应开括号,如果是,则弹出堆栈,否则打破循环并返回假的。这是有效的,但我们可以优化空间,可以是恒定的额外空间,我知道时间复杂度不能小于O(n),因为我们必须查看每个角色。
所以我的问题是我们可以在O(1)空间中解决这个问题吗?
答案 0 :(得分:12)
参考 Matthieu M。的优秀答案,这是C#中的一个实现,看起来效果很好。
/// <summary>
/// Checks to see if brackets are well formed.
/// Passes "Valid parentheses" challenge on www.codeeval.com,
/// which is a programming challenge site much like www.projecteuler.net.
/// </summary>
/// <param name="input">Input string, consisting of nothing but various types of brackets.</param>
/// <returns>True if brackets are well formed, false if not.</returns>
static bool IsWellFormedBrackets(string input)
{
string previous = "";
while (input.Length != previous.Length)
{
previous = input;
input = input
.Replace("()", String.Empty)
.Replace("[]", String.Empty)
.Replace("{}", String.Empty);
}
return (input.Length == 0);
}
基本上,它所做的就是删除一对括号,直到没有任何一个删除;如果还有什么东西,那么托架就不会很好。
格式良好的括号的例子:
()[]
{()[]}
格式错误的括号示例:
([)]
{()[}]
答案 1 :(得分:11)
实际上,由于Ritchie和Springsteel有一个确定性的对数空间算法:http://dx.doi.org/10.1016/S0019-9958(72)90205-7( paywalled,抱歉不在线)。由于我们需要日志位来索引字符串,因此这是空间最优的。
如果您愿意接受片面错误,那么有一种算法使用n polylog(n)时间和polylog(n)空间:http://www.eccc.uni-trier.de/report/2009/119/
答案 2 :(得分:6)
如果输入是只读的,我认为我们不能做O(1)空间。众所周知,任何O(1)空间可判定语言都是规则的(即可写为正则表达式)。你拥有的字符串集不是常规语言。
当然,这是关于图灵机。我希望固定字RAM机也是如此。
答案 3 :(得分:3)
编辑:虽然简单,但就角色比较而言,此算法实际上是O(n ^ 2)。为了演示它,可以简单地生成一个字符串'(' * n + ')' * n
。
我有一个简单但可能是错误的想法,我会接受你的批评。
这是一种破坏性算法,这意味着如果你需要字符串它就无济于事(因为你需要将其复制下来)。
否则,算法使用当前字符串中的简单索引。
这个想法是一个接一个地删除对:
([{}()])
([()])
([])
()
empty
- &gt; OK
这是基于一个简单的事实,即如果我们有匹配的对,那么至少有一个是()
形式,其间没有任何配对字符。
算法:
i := 0
i
找到匹配的对。如果未找到,则该字符串无效。如果找到一个,请i
为第一个字符的索引。[i:i+1]
i
位于字符串的末尾,而字符串不为空,则表示失败。[i-1:i]
是匹配对,i := i-1
并返回3。该算法的复杂度为O(n)
因为:
i
无法无限增长)它在空间中是O(1)
,因为只需要索引。
当然,如果你不能销毁字符串,那么你将不得不复制它,那就是O(n)
在太空中,所以没有真正的好处!
当然,除非我在某个地方犯了很大的错误......也许有人可以使用最初的想法(在某处有一对)来更好地发挥作用。
答案 4 :(得分:2)
http://www.sureinterview.com/shwqst/112007
用堆栈解决这个问题是很自然的。
如果仅使用'('和')',则不需要堆栈。我们只需要维持一个不匹配左边的计数器'('。如果计数器在匹配期间总是非负的并且在结束时为零,则表达式有效。
一般情况下,虽然堆栈仍然是必需的,但是通过使用不匹配的大括号计数器可以减少堆栈的深度。
答案 5 :(得分:2)
我怀疑你会找到一个更好的解决方案,因为即使你使用内部函数来regexp或计算出现次数,它们仍然有O(...)成本。我说你的解决方案是最好的:)
要优化空间,你可以在你的堆栈上进行一些游程编码,但我怀疑它会给你带来很大的收获,除非像{{{{{{{{{{}}}}}}}}}}
这样的情况。
答案 6 :(得分:2)
这是一个有效的java代码,我从字符串表达式中筛选括号,然后通过用空值替换格式良好的大括号来检查格式良好
示例input = (a+{b+c}-[d-e])+[f]-[g]
FilterBrackets将输出= ({}[])[][]
然后我检查是否良好。
欢迎评论。
public class ParanString {
public static void main(String[] args) {
String s = FilterBrackets("(a+{b+c}-[d-e])[][]");
while ((s.length()!=0) && (s.contains("[]")||s.contains("()")||s.contains("{}")))
{
//System.out.println(s.length());
//System.out.println(s);
s = s.replace("[]", "");
s = s.replace("()", "");
s = s.replace("{}", "");
}
if(s.length()==0)
{
System.out.println("Well Formed");
}
else
{
System.out.println("Not Well Formed");
}
}
public static String FilterBrackets(String str)
{
int len=str.length();
char arr[] = str.toCharArray();
String filter = "";
for (int i = 0; i < len; i++)
{
if ((arr[i]=='(') || (arr[i]==')') || (arr[i]=='[') || (arr[i]==']') || (arr[i]=='{') || (arr[i]=='}'))
{
filter=filter+arr[i];
}
}
return filter;
}
}
答案 7 :(得分:2)
Sbusidan的答案的以下修改是O( n 2 )时间复杂但是O(log n 我简单地说空间。
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
char opposite(char bracket) {
switch(bracket) {
case '[':
return ']';
case '(':
return ')';
}
}
bool is_balanced(int length, char *s) {
int depth, target_depth, index;
char target_bracket;
if(length % 2 != 0) {
return false;
}
for(target_depth = length/2; target_depth > 0; target_depth--) {
depth=0;
for(index = 0; index < length; index++) {
switch(s[index]) {
case '(':
case '[':
depth++;
if(depth == target_depth) target_bracket = opposite(s[index]);
break;
case ')':
case ']':
if(depth == 0) return false;
if(depth == target_depth && s[index] != target_bracket) return false;
depth--;
break;
}
}
}
}
void main(char* argv[]) {
char input[] = "([)[(])]";
char *balanced = is_balanced(strlen(input), input) ? "balanced" : "imbalanced";
printf("%s is %s.\n", input, balanced);
}
答案 8 :(得分:1)
如果你可以覆盖输入字符串(在我设想的用例中不合理,但是哎呀...)你可以在恒定的空间内完成它,尽管我相信时间要求达到 O( ñ 2 )
像这样:
string s = input
char c = null
int i=0
do
if s[i] isAOpenChar()
c = s[i]
else if
c = isACloseChar()
if closeMatchesOpen(s[i],c)
erase s[i]
while s[--i] != c ;
erase s[i]
c == null
i = 0; // Not optimal! It would be better to back up until you find an opening character
else
return fail
end if
while (s[++i] != EOS)
if c==null
return pass
else
return fail
这个的本质是将输入的早期部分用作堆栈。
答案 9 :(得分:1)
我知道我这个派对有点晚了;这也是我在StackOverflow上的第一篇文章。
但是当我查看答案时,我想我可以提出更好的解决方案。
所以我的解决方案是使用一些指针
它甚至不必使用任何RAM存储器,因为可以使用寄存器。
我没有测试过代码;它是在飞行中写的。
你需要修复我的拼写错误并进行调试,但我相信你会明白这个想法。
内存使用:大多数情况下只有CPU注册 CPU使用率:取决于,但大约是读取字符串所需时间的两倍 修改内存:否。
b:字符串 b eginning,e:string e nd。
l: l eft位置,r: r 位置。
c: c har,m: m atch char
如果r到达字符串的末尾,我们就取得了成功
l从r向后退回b。
每当r遇到新的起始类型时,设置l = r。
当我到达b时,我们完成了块;跳到下一个街区的开头。
const char *chk(const char *b, int len) /* option 2: remove int len */
{
char c, m;
const char *l, *r;
e = &b[len]; /* option 2: remove. */
l = b;
r = b;
while(r < e) /* option 2: change to while(1) */
{
c = *r++;
/* option 2: if(0 == c) break; */
if('(' == c || '{' == c || '[' == c)
{
l = r;
}
else if(')' == c || ']' == c || '}' == c)
{
/* find 'previous' starting brace */
m = 0;
while(l > b && '(' != m && '[' != m && '{' != m)
{
m = *--l;
}
/* now check if we have the correct one: */
if(((m & 1) + 1 + m) != c) /* cryptic: convert starting kind to ending kind and match with c */
{
return(r - 1); /* point to error */
}
if(l <= b) /* did we reach the beginning of this block ? */
{
b = r; /* set new beginning to 'head' */
l = b; /* obsolete: make left is in range. */
}
}
}
m = 0;
while(l > b && '(' != m && '[' != m && '{' != m)
{
m = *--l;
}
return(m ? l : NULL); /* NULL-pointer for OK */
}
在考虑了这种方法一段时间之后,我意识到它现在不会起作用 问题是,如果你有“[()()]”,它会在到达']'时失败 但是,我不会删除建议的解决方案,而是将其留在这里,因为它实际上并非不可能,但它确实需要进行一些修改。
答案 10 :(得分:0)
/**
*
* @author madhusudan
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
new Main().validateBraces("()()()()(((((())))))()()()()()()()()");
// TODO code application logic here
}
/**
* @Use this method to validate braces
*/
public void validateBraces(String teststr)
{
StringBuffer teststr1=new StringBuffer(teststr);
int ind=-1;
for(int i=0;i<teststr1.length();)
{
if(teststr1.length()<1)
break;
char ch=teststr1.charAt(0);
if(isClose(ch))
break;
else if(isOpen(ch))
{
ind=teststr1.indexOf(")", i);
if(ind==-1)
break;
teststr1=teststr1.deleteCharAt(ind).deleteCharAt(i);
}
else if(isClose(ch))
{
teststr1=deleteOpenBraces(teststr1,0,i);
}
}
if(teststr1.length()>0)
{
System.out.println("Invalid");
}else
{
System.out.println("Valid");
}
}
public boolean isOpen(char ch)
{
if("(".equals(Character.toString(ch)))
{
return true;
}else
return false;
}
public boolean isClose(char ch)
{
if(")".equals(Character.toString(ch)))
{
return true;
}else
return false;
}
public StringBuffer deleteOpenBraces(StringBuffer str,int start,int end)
{
char ar[]=str.toString().toCharArray();
for(int i=start;i<end;i++)
{
if("(".equals(ar[i]))
str=str.deleteCharAt(i).deleteCharAt(end);
break;
}
return str;
}
}
答案 11 :(得分:0)
您可以使用两个指针来检查字符串的字符,而不是将大括号放入堆栈。一个从字符串的开头开始,另一个从字符串的结尾开始。
之类的东西bool isValid(char* s) {
start = find_first_brace(s);
end = find_last_brace(s);
while (start <= end) {
if (!IsPair(start,end)) return false;
// move the pointer forward until reach a brace
start = find_next_brace(start);
// move the pointer backward until reach a brace
end = find_prev_brace(end);
}
return true;
}
请注意,有些角落案件没有处理。
答案 12 :(得分:0)
我认为您可以实现O(n)算法。只需要为每种类型初始化一个计数器变量:卷曲,方形和普通括号。之后应该迭代字符串,如果括号打开则应增加相应的计数器,否则减少它。如果计数器为负,则返回false。之后我认为你可以实现一个O(n)算法。只需要为每种类型初始化一个计数器变量:卷曲,方形和普通括号。之后应该迭代字符串,如果括号打开则应增加相应的计数器,否则减少它。如果计数器为负,则返回false。计算所有括号后,应检查所有计数器是否为零。在这种情况下,字符串有效,您应该返回true。
答案 13 :(得分:0)
这是我解决问题的方法。 O(n)是时间的复杂性,没有空间的复杂性。 C中的代码。
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool checkBraket(char *s)
{
int curly = 0, rounded = 0, squre = 0;
int i = 0;
char ch = s[0];
while (ch != '\0')
{
if (ch == '{') curly++;
if (ch == '}') {
if (curly == 0) {
return false;
} else {
curly--; }
}
if (ch == '[') squre++;
if (ch == ']') {
if (squre == 0) {
return false;
} else {
squre--;
}
}
if (ch == '(') rounded++;
if (ch == ')') {
if (rounded == 0) {
return false;
} else {
rounded--;
}
}
i++;
ch = s[i];
}
if (curly == 0 && rounded == 0 && squre == 0){
return true;
}
else {
return false;
}
}
void main()
{
char mystring[] = "{{{{{[(())}}]}}}";
int answer = checkBraket(mystring);
printf("my answer is %d\n", answer);
return;
}
答案 14 :(得分:0)
您可以提供该值并检查其是否为有效值,它将打印为YES,否则将打印NO
static void Main(string[] args)
{
string value = "(((([{[(}]}]))))";
List<string> jj = new List<string>();
if (!(value.Length % 2 == 0))
{
Console.WriteLine("NO");
}
else
{
bool isValid = true;
List<string> items = new List<string>();
for (int i = 0; i < value.Length; i++)
{
string item = value.Substring(i, 1);
if (item == "(" || item == "{" || item == "[")
{
items.Add(item);
}
else
{
string openItem = items[items.Count - 1];
if (((item == ")" && openItem == "(")) || (item == "}" && openItem == "{") || (item == "]" && openItem == "["))
{
items.RemoveAt(items.Count - 1);
}
else
{
isValid = false;
break;
}
}
}
if (isValid)
{
Console.WriteLine("Yes");
}
else
{
Console.WriteLine("NO");
}
}
Console.ReadKey();
}
答案 15 :(得分:0)
var verify = function(text)
{
var symbolsArray = ['[]', '()', '<>'];
var symbolReg = function(n)
{
var reg = [];
for (var i = 0; i < symbolsArray.length; i++) {
reg.push('\\' + symbolsArray[i][n]);
}
return new RegExp('(' + reg.join('|') + ')','g');
};
// openReg matches '(', '[' and '<' and return true or false
var openReg = symbolReg(0);
// closeReg matches ')', ']' and '>' and return true or false
var closeReg = symbolReg(1);
// nestTest matches openSymbol+anyChar+closeSymbol
// and returns an obj with the match str and it's start index
var nestTest = function(symbols, text)
{
var open = symbols[0]
, close = symbols[1]
, reg = new RegExp('(\\' + open + ')([\\s\\S])*(\\' + close + ')','g')
, test = reg.exec(text);
if (test) return {
start: test.index,
str: test[0]
};
else return false;
};
var recursiveCheck = function(text)
{
var i, nestTests = [], test, symbols;
// nestTest with each symbol
for (i = 0; i < symbolsArray.length; i++)
{
symbols = symbolsArray[i];
test = nestTest(symbols, text);
if (test) nestTests.push(test);
}
// sort tests by start index
nestTests.sort(function(a, b)
{
return a.start - b.start;
});
if (nestTests.length)
{
// build nest data: calculate match end index
for (i = 0; i < nestTests.length; i++)
{
test = nestTests[i];
var end = test.start + ( (test.str) ? test.str.length : 0 );
nestTests[i].end = end;
var last = (nestTests[i + 1]) ? nestTests[i + 1].index : text.length;
nestTests[i].pos = text.substring(end, last);
}
for (i = 0; i < nestTests.length; i++)
{
test = nestTests[i];
// recursive checks what's after the nest
if (test.pos.length && !recursiveCheck(test.pos)) return false;
// recursive checks what's in the nest
if (test.str.length) {
test.str = test.str.substring(1, test.str.length - 1);
return recursiveCheck(test.str);
} else return true;
}
} else {
// if no nests then check for orphan symbols
var closeTest = closeReg.test(text);
var openTest = openReg.test(text);
return !(closeTest || openTest);
}
};
return recursiveCheck(text);
};
答案 16 :(得分:0)
使用c#OOPS编程......小而简单的解决方案
Console.WriteLine("Enter the string");
string str = Console.ReadLine();
int length = str.Length;
if (length % 2 == 0)
{
while (length > 0 && str.Length > 0)
{
for (int i = 0; i < str.Length; i++)
{
if (i + 1 < str.Length)
{
switch (str[i])
{
case '{':
if (str[i + 1] == '}')
str = str.Remove(i, 2);
break;
case '(':
if (str[i + 1] == ')')
str = str.Remove(i, 2);
break;
case '[':
if (str[i + 1] == ']')
str = str.Remove(i, 2);
break;
}
}
}
length--;
}
if(str.Length > 0)
Console.WriteLine("Invalid input");
else
Console.WriteLine("Valid input");
}
else
Console.WriteLine("Invalid input");
Console.ReadKey();