# 678. Valid Parenthesis String

**<font color=red>难度: Medium</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/valid-parenthesis-string/description/

> 内容描述

```

Given a string containing only three types of characters: '(', ')' and '*', write a function to check whether this string is valid. We define the validity of a string by these rules:

Any left parenthesis '(' must have a corresponding right parenthesis ')'.
Any right parenthesis ')' must have a corresponding left parenthesis '('.
Left parenthesis '(' must go before the corresponding right parenthesis ')'.
'*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string.
An empty string is also valid.
Example 1:
Input: "()"
Output: True
Example 2:
Input: "(*)"
Output: True
Example 3:
Input: "(*))"
Output: True
Note:
The string size will be in the range [1, 100].
```

## 解题方案


> 思路 1
******- 时间复杂度: O(N^3)******- 空间复杂度: O(N^2)******

```
Let dp[i][j] be true if and only if the interval s[i], s[i+1], ..., s[j] can be made valid. Then dp[i][j] is true only if:

s[i] is '*', and the interval s[i+1], s[i+2], ..., s[j] can be made valid;

or, s[i] can be made to be '(', and there is some k in [i+1, j] such that s[k] can be made to be ')', plus the two intervals cut by s[k] (s[i+1: k] and s[k+1: j+1]) can be made valid;
```
因为文章要从垃圾算法写到好的算法，这不是我的第一解法

但是是真的垃圾，才beats 2.75%
```python
class Solution(object):
    def checkValidString(self, s):
        """
        :type s: str
        :rtype: bool
        """
        if not s or len(s) == 0:
            return True
        
        LEFTY, RIGHTY = '(*', ')*'

        n = len(s)
        dp = [[0] * n for _ in s]
        for i in range(n):
            if s[i] == '*':
                dp[i][i] = 1
            if i < n-1 and s[i] in LEFTY and s[i+1] in RIGHTY:
                dp[i][i+1] = 1
        for j in range(n):
            for i in range(j-2, -1, -1):
                if s[i] == '*' and dp[i+1][j]:
                    dp[i][j] = 1
                elif s[i] in LEFTY:
                    for k in range(i+1, j+1):
                        if s[k] in RIGHTY and \
                                (k == i+1 or dp[i+1][k-1]) and \
                                (k == j or dp[k+1][j]):
                            dp[i][j] = 1
                            
        return True if dp[0][-1] else False
```

或者后面的循环按照子串的长度size来遍历，

```python
class Solution(object):
    def checkValidString(self, s):
        """
        :type s: str
        :rtype: bool
        """
        if not s or len(s) == 0:
            return True
        
        LEFTY, RIGHTY = '(*', ')*'

        n = len(s)
        dp = [[False] * n for _ in s]
        for i in range(n):
            if s[i] == '*':
                dp[i][i] = True
            if i < n-1 and s[i] in LEFTY and s[i+1] in RIGHTY:
                dp[i][i+1] = True

        for size in range(2, n):
            for i in range(n - size):
                if s[i] == '*' and dp[i+1][i+size]:
                    dp[i][i+size] = True
                elif s[i] in LEFTY:
                    for k in range(i+1, i+size+1):
                        if (s[k] in RIGHTY and \
                                (k == i+1 or dp[i+1][k-1]) and \
                                (k == i+size or dp[k+1][i+size])):
                            dp[i][i+size] = True

        return dp[0][-1]
```

> 思路 2
******- 时间复杂度: O(N)******- 空间复杂度: O(N)******

这是我的最初方法

- Keep track of indices for unmatched '(' and available '*'. 
- After the loop we can check whether the index of any remaining '*' can match the unmatched '('. 
- If the index of star is greater than the index of '(' then they can match.

beats 100%


```python
class Solution(object):
    def checkValidString(self, s):
        """
        :type s: str
        :rtype: bool
        """
        if not s or len(s) == 0:
            return True
        
        stack, star = [], []
        for i in range(len(s)):
            if s[i] == '(':
                stack.append(i)
            elif s[i] == ')':
                if stack:
                    stack.pop()
                elif star:
                    star.pop() 
                else:
                    return False
            else:
                star.append(i)

        while stack and star:
            # 从后面开始比较，如果最后出现的'('的index比最后出现的'*'更大，那其实这个匹配不了，比如'*('
            if stack[-1] > star[-1]: 
                return False
            else:
                stack.pop()
                star.pop()
        
        return not stack
```

> 思路 3
******- 时间复杂度: O(N)******- 空间复杂度: O(1)******


看到别人的解法，真的🐂p，贪心算法

```
We know that at each point in the string, '( 'count should never be bigger than ')' count, and in the end the difference of two counts should be 0.
So we have two boundaries: lowerBond and higherBound, which respectively represents the minimun possible difference and maximum possbile difference, as long as

higherBound is never below 0.
in the end 0 is between lowerBond and higherBound
in the string, when lowerbound is < 0, we make it back to 0.
We know that the string is valid.
```

beats 100%

```python
class Solution(object):
    def checkValidString(self, s):
        """
        :type s: str
        :rtype: bool
        """
        lower_bound = higher_bound = 0
        for c in s:
            lower_bound += 1 if c == '(' else -1
            higher_bound += 1 if c != ')' else -1
            if higher_bound < 0: 
                return False
            lower_bound = max(lower_bound, 0)

        return lower_bound == 0
```

