301. Remove Invalid Parentheses

題目鏈接:https://leetcode.com/problems/remove-invalid-parentheses/
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results.

Note: The input string may contain letters other than the parentheses ( and ).

Examples:

"()())()" -> ["()()()", "(())()"]
"(a)())()" -> ["(a)()()", "(a())()"]
")(" -> [""]

思路:

我們都知道判斷一個字符串是否有效,通常使用stack。這裏還有一種更簡單的方法是使用一個計數器。
這個計數器count表示’(’ 和’)’之間的差值,遇到’(’ count 加一,遇到’)’count 減一。當count爲負數的時候,說明在當前前綴字符串中’)’大於’(‘,即無效。

使用棧,更多是判斷是否有效,還要把有效的子串還原出來,就顯得不太方便。

對於一個字符串,在任何時候如果’)’的個數多於左括號,則說明從開始到現在位置必然可以刪除一個’)’.而這段子串可能包含多個’)’,刪除哪一個呢?當然刪除任何一個都可以.例如對於()())(),從開頭到s[4]位置構成的子串多了一個右括號,因此我們需要刪掉一個,而這個子串有三個右括號,但是隻會產生2個結果,也就是會有一個重複值.所以在刪除括號的時候,爲保證不會產生重複值,需要記錄一個最後刪除的位置,這樣可以使得在接下來刪除的時候只刪除這個位置之後的值.這樣我們可以使得當前這一段子串不再包含多餘的右括號了.這樣我們可以刪除了一個右括號之後合法的子串與後面還沒有檢查過的子串組成一個新的字符串重新開始檢查.直到不再含有非法的右括號.
但是還有一種情況是包含了多餘的左括號,一種直觀的方法是從右向左再按照上面的方法處理一遍左括號.但是將數組逆置之後就可以重用上面的算法了.
所以總的思路就是先對字符串進行處理使得其不再含有非法右括號,然後將其翻轉以後再檢查是否含有非法的左括號.最後左右括號都檢查完之後都合法就是我們要的答案了.
時間複雜度應該是O(n^2),但是對與這道題來說,這是一個非常高效的方法了,運行時間0ms,比絕大多數算法都要高效.

爲什麼加記錄最後刪除的位置,可以避免重複?

/**
重點還是代碼中的這個判斷條件,是遍歷從上次最後刪除的位置到當前點之間的所有`)`如果 j ==last,可以刪除這個不會和之前重複,如果 j!=last還需要s[j-1]!= ')' 因爲如果s[j-1]==')'的話,刪除j和刪除j-1 的情況就會一樣,導致重複。(當前last指向,刪除節點的後面一個點,因爲刪除後,後半段字符串前移了一位) 
**/

    if(s[j] == ch && (j ==last || s[j-1]!= ch))  
          DFS(s.substr(0, j)+s.substr(j+1), ch, j);  

java:

    public static List<String> removeInvalidParentheses(String s) {

        List<String> res = new ArrayList<>(null);

        if (s == null || s.length() == 0)
            return res;

        DFS(res,s,0,0,new char[]{'(',')'});


        return res;
    }

    private static void DFS(List<String> res, String s, int last_i, int last_j, char[] chars) {

        for (int count = 0, i = last_i; i < s.length() ; i++)
        {
            if (s.charAt(i) == chars[0]) count ++;
            if (s.charAt(i) == chars[1]) count --;
            if (count >= 0) continue;                                   //靠這裏跳出for循環的
            for (int j = last_j ; j < s.length() ; j++)
            {
                if (s.charAt(j) == chars[1] && (j==last_j || s.charAt(j-1)!=chars[1]))
                    DFS(res,s.substring(0,j)+s.substring(j+1,s.length()),i,j,chars);

            }

//從這裏return說明對於當前非法的字符串,已經遍歷完最後一個刪除位置到當前位置所有可能的刪除的情況
        }

        String reversed = new StringBuilder(s).reverse().toString();
        if (chars[0] == '(') 
        // finished left to right,如果等於 '('說明進行完第一次遍歷,還需要第二次
            DFS(res, reversed, 0, 0, new char[]{')', '('});
        else // finished right to left
            res.add(reversed);



    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章