在O(n)时间内查找字符串中最长有效括号序列的长度

时间:2014-09-20 19:06:15

标签: algorithm language-agnostic dynamic-programming code-complexity

我的朋友在接受采访时遇到了一个问题,他被告知有一个O(n)解决方案。但是,我们都不能想到它。这是一个问题:

有一个只包含()的字符串,找到最长有效括号子字符串的长度,该子字符串应该格式正确。

例如")()())",最长的有效括号为()(),长度为4。

我用动态编程想出来了,但它不是O(n)。有什么想法吗?

public int getLongestLen(String s) {
    if (s == null || s.length() == 0)
        return 0;

    int len = s.length(), maxLen = 0;
    boolean[][] isValid = new boolean[len][len];
    for (int l = 2; l < len; l *= 2)
        for (int start = 0; start <= len - l; start++) {
            if ((s.charAt(start) == '(' && s.charAt(start + l - 1) == ')') && 
                (l == 2 || isValid[start+1][start+l-2])) {
                    isValid[start][start+l-1] = true;
                    maxLen = Math.max(maxLen, l);
                }
        }

    return maxLen;
}

10 个答案:

答案 0 :(得分:16)

之前我做过这个问题,在压力下提出O(n)解决方案并不容易。这是它,用堆栈解决。

   private int getLongestLenByStack(String s) {
    //use last to store the last matched index
    int len = s.length(), maxLen = 0, last = -1;
    if (len == 0 || len == 1)
        return 0;

    //use this stack to store the index of '('
    Stack<Integer> stack = new Stack<Integer>();
    for (int i = 0; i < len; i++) {
        if (s.charAt(i) == '(') 
            stack.push(i);
        else {
            //if stack is empty, it means that we already found a complete valid combo
            //update the last index.
            if (stack.isEmpty()) {
                last = i;        
            } else {
                stack.pop();
                //found a complete valid combo and calculate max length
                if (stack.isEmpty()) 
                    maxLen = Math.max(maxLen, i - last);
                else
                //calculate current max length
                    maxLen = Math.max(maxLen, i - stack.peek());
            }
        }
    }

    return maxLen;
}

答案 1 :(得分:2)

您可以分别为每个左括号/近括号递增/递减int变量。跟踪此类有效操作的数量(变量不低于0的位置)作为当前长度,并跟踪最长的操作 - 例如最大值。

public int getLongestLen(String s) {
    if (s == null || s.length() == 0) {
        return 0;       
    }

    int stack = 0;
    int counter = 0;
    int max = 0;

    for (Character c: s.toCharArray()) {
        if (c == '(') {
            stack++;
        }
        if (c == ')') {
            stack--;
        }
        if (stack >= 0) {
            counter++;
        }
        if (stack < 0) {
            counter = 0;
            stack = 0;
        }
        if (counter > max && stack == 0) {
            max = counter;
        }
    }

    return max;
}

答案 2 :(得分:2)

我们需要将先前起始括号的索引存储在堆栈中。

我们将堆栈的第一个元素作为特殊元素推送为&#34; -1&#34;或索引中不会出现的任何其他数字。

现在我们遍历字符串,当我们遇到&#34;(&#34;括号我们推它们,否则当我们遇到&#34;)&#34;我们首先弹出它们和

如果stack不为空,我们通过获取结果的最大值(初始化为零)以及堆栈顶部的当前索引和索引之间的差异来找到该点之前的最大有效子字符串的长度。

否则如果stack为空,我们推送索引。

int result=0;
stack<int> s1;
s1.push(-1);
for(int i=0;i<s.size();++i)
{
    if(s[i]=='(')
        s1.push(i);

    else if(s[i]==')')
    {
        s1.pop();

        if(!s1.empty())
            result=max(result,i-s1.top());
        else
            s1.push(i);
    }
}
cout<<result<<endl;

这里&#39;是字符串和&#39; s1&#39;是堆栈。

答案 3 :(得分:1)

算法:Entire code on GitHub
1.添加到堆栈
   1.1初始化为-1,句柄))没有((
2.当你看到)从堆栈中弹出时    2.a如果堆栈大小== 0(不匹配),则推送当前索引值
   2.b如果堆栈大小> 0(匹配),通过从当前索引中减去顶部值的索引得到最大长度(完全邪恶!)

def longestMatchingParenthesis(a):
    pstack = []        #index position of left parenthesis
    pstack.append(-1)  #default value; handles ) without ( and when match adds up to 2!
    stack_size = 1 
    result = 0
    for i in range(0,len(a)):
            if a[i] == '(':
                    pstack.append(i) #Append current index
                    stack_size += 1
            else:    # handle )
                    pstack.pop()
                    stack_size -= 1
                    #determine length of longest match!
                    if stack_size > 0:
                            #difference of current index - index at top of the stack (yet to be matched)
                            result = max(result, i - pstack[-1])
                    else:
                            #stack size == 0, append current index
                            pstack.append(i)
                            stack_size += 1 
    return result

a = ["()()()", "", "((((", "(((()", "(((())(", "()(()" ,"()(())"]
for x in a:
    print("%s = %s" % (x,longestMatchingParenthesis(x)))

#output
()()() = 6
= 0
(((( = 0
(((() = 2
(((())( = 4
()(() = 2
()(()) = 6

答案 4 :(得分:1)

如果您愿意采用动态方法来查找有效元素,然后尝试通过检查相邻元素来增大其大小,则无需常规使用堆栈就可以实现O(n)。 首先我们找到一个'()' 然后,我们尝试找到包含以下内容的更长的字符串:

可能性是:

  • '()'),其中我们检查之前的索引和之后的索引
  • '()'(),我们在其中检查下一个有效单位,以免在搜索中不再重复。

接下来,我们在每个循环中更新当前检查的开始和结束索引

在有效字符串的末尾,检查到目前为止最大长度的当前计数器,并在必要时进行更新。 链接到GitHub Click Here上的Python代码。

答案 5 :(得分:0)

刚刚提出解决方案,如果有任何错误请做评论

count = 0 //stores the number of longest valid paranthesis
empty stack s
arr[]; //contains the string which has the input, something like ())(()(
while(i<sizeof(arr))
{
    if(a[i] == '(' )
    {
        if(top == ')' ) //top of a stack,
        {
            count = 0;
            push a[i] in stack;
        }
    }
    else
    {
        if(top == '(' )
        {
            count+=2;
            pop from stack;
        }
        else
        {
            push a[i] in stack;
        }
    }
}

打印计数

答案 6 :(得分:0)

以下解决方案具有O(n)时间复杂度和O(1)空间复杂度。

这非常直观。

我们首先从左到右遍历字符串,寻找最长的有效子字符串,使用&#39; count&#39;通常用于检查parens有效性的方法。在执行此操作时,我们还会记录此子字符串的最大长度(如果找到)。

然后,我们从右到左做同样的事

算法如下:

// Initialize variables

1. count = 0, len = 0, max_len_so_far = 0


// Check for longest valid string of parens while traversing from left to right

2. iterate over input string from left to right:
   - len += 1
   - if next character is '(',
         count += 1
   - if next character is ')',
         count -= 1
   - if (count == 0 and len > max_len_so_far),
         max_len_so_far = len
   - if (count < 0),
         len = 0, count = 0


// Set count and len to zero again, but leave max_len_so_far untouched

3. count = 0, len = 0


// Now do a very similar thing while traversing from right to left
// (Though do observe the switched '(' and ')' in the code below)

4. iterate over input string from right to left:
   - len += 1
   - if next character is ')',
         count += 1
   - if next character is '(',
         count -= 1
   - if (count == 0 and len > max_len_so_far),
         max_len_so_far = len
   - if (count < 0),
         len = 0, count = 0


// max_len_so_far is now our required answer

5. Finally,
   return max_len_so_far



例如,考虑字符串

"((())"



假设该字符串为零索引。

我们先从左到右。

因此,在索引0处,计数将为1,然后在索引1处为2,在索引处为2处,在索引处为2处,以及在索引处为3处为1处。在此步骤中,max_len甚至不会改变,因为计数是再也不会是0了。

然后我们从右到左。

在索引4处,count为1,然后在索引3处为2,然后在索引2处为1,然后在索引1处为0。在此处,len为4且max_len_so_far = 0,因此我们设置max_len = 4。登记/> 然后,在索引0处,计数为1.

此时,我们停止并返回4,这确实是正确的答案。

正确性的证明留给读者作为包容性的练习。

注意:这个算法也可以非常简单地调整,以返回括号本身的最长有效子串,而不仅仅是它的长度。

答案 7 :(得分:0)

public static void main(String[] args) {
    String s="))((())";
    String finalString="";
    for(int i=0;i<s.length();i++){

         if (s.charAt(i) == '('&& s.charAt(i+1) == ')') {
         String ss= s.substring(i, i+2);
         finalString=finalString+ss;
        // System.out.println(ss);
         }

    }
    System.out.println(finalString.length());
}

答案 8 :(得分:0)

使用动态编程来存储和重复使用已经计算的结果

def longest_valid_paranthesis(str):
    l = len(str)
    dp = [0]*len(str)
    for i in range(l):
        if str[i] == '(':
            dp[i] = 0
        elif str[i-1] == '(':
            dp[i] = dp[i-2] + 2
        elif str[i - dp[i-1] - 1] == '(':
            dp[i] = dp[i-1] + 2 + dp[i - (dp[i-1] + 2)]

答案 9 :(得分:-1)

请找到最简单的工作解决方案。

#include "stdafx.h"
#include <string>
#include <vector>
#include <iostream>
#include <stack>
int findMaxLen(string str)
{
int n = str.length();

// Create a stack.
stack<char> stk;


// Initialize result
int result = 0;

// Traverse all characters of given string
for (int i = 0; i<n; i++)
{
    // If opening bracket, push char '(' of it
    if (str[i] == '(')
        stk.push(str[i]);

    else // If closing bracket, i.e.,str[i] = ')'
    {
        if (!stk.empty())
        {
            stk.pop();
            result += 2;                
        }           
    }
}

return result;
}