14

我的朋友在一次采访中遇到了一个问题,他被告知有一个 O(n) 的解决方案。然而,我们谁也想不出来。这是问题:

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

例如")()())",最长有效括号为()(),长度为 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;
}
4

9 回答 9

22

这个问题我之前做过,在压力下想出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;
}
于 2014-09-20T19:13:06.020 回答
4

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

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

现在我们遍历字符串,当我们遇到 "(" 大括号时我们推送它们,否则当我们遇到 ")" 我们首先弹出它们并

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

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

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;

这里's'是字符串,'s1'是堆栈。

于 2017-02-16T20:07:40.183 回答
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;
}
于 2014-09-20T19:23:53.173 回答
1

算法: GitHub
1 上的整个代码。添加到堆栈
1.1 初始化 -1,handle )) 没有 ((
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
于 2016-03-12T06:53:56.233 回答
1

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

可能性是:

  • ( '()' ) 我们检查之前的索引和之后的索引
  • '()' () 我们检查下一个有效单元,这样我们就不会在搜索中重复它。

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

在有效字符串的末尾,检查当前计数器的最大长度,并在必要时更新。GitHub 上 Python 代码的链接单击此处

于 2018-08-10T15:53:49.660 回答
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;
        }
    }
}

打印计数

于 2014-09-20T19:26:07.147 回答
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());
}
于 2019-06-22T11:22:39.453 回答
0

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

它非常直观。

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

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


算法如下:

// 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 处,count 将为 1,然后在索引 1 处为 2,在索引 2 处为 3,在索引 3 处为 2,在索引 4 处为 1。在这一步中,max_len 甚至不会改变,因为 count 永远不会为 0再次。

然后我们从右到左。

在索引 4 处,count 为 1,然后在索引 3 处为 2,然后在索引 2 处为 1,然后在索引 1 处为 0。
此时,len 为 4,max_len_so_far=0,因此我们设置 max_len = 4。
然后,在索引 0 处,count为1。

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



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

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

于 2017-07-30T08:19:05.167 回答
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)]
于 2020-11-30T09:16:43.420 回答