# String ###### tags: `Study_aboard` ## Basic ### Roman to integer ![](https://i.imgur.com/Q7EYxT5.png) ![](https://i.imgur.com/8EyW5qR.png) easy one ```cpp class Solution { public: int romanToInt(string s) { int res = 0; unordered_map<char, int> m{{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}}; for (int i = 0; i < s.size(); ++i) { int val = m[s[i]]; if (i == s.size() - 1 || m[s[i+1]] <= m[s[i]]) res += val; else res -= val; } return res; } }; ``` ### Integer to roman ![](https://i.imgur.com/kl9R6Z4.png) ![](https://i.imgur.com/7ZMcqx5.png) Initial: 將特殊數字4,9,40等也加入str中,並由大至小開始做 ```cpp class Solution { public: string intToRoman(int num) { string res = ""; vector<int> val{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; vector<string> str{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; for (int i = 0; i < val.size(); ++i) { while (num >= val[i]) { num -= val[i]; res += str[i]; } } return res; } }; ``` Problem: 當數字很大時可能會出現問題 Sol: 用取商法 ex: 將千位的數字取出來(/1000)後,若為0~3 or 5~8,則將M疊加0~3 or 5~8次。若為,則為CM。 (M不會出現4,因為數字限定<3999) ```cpp class Solution { public: string intToRoman(int num) { string res = ""; vector<char> roman{'M', 'D', 'C', 'L', 'X', 'V', 'I'}; vector<int> value{1000, 500, 100, 50, 10, 5, 1}; for (int n = 0; n < 7; n += 2) { int x = num / value[n]; if (x < 4) { for (int i = 1; i <= x; ++i) res += roman[n]; } else if (x == 4) { res = res + roman[n] + roman[n - 1]; } else if (x > 4 && x < 9) { res += roman[n - 1]; for (int i = 6; i <= x; ++i) res += roman[n]; } else if (x == 9) { res = res + roman[n] + roman[n - 2]; } num %= value[n]; } return res; } }; ``` ### Reverse words in a string ![](https://i.imgur.com/LZnBaxe.png) ![](https://i.imgur.com/dThkLyG.png) ![](https://i.imgur.com/xHjkUXT.png) Initial: use a stack to reverse it ```cpp class Solution { public: string reverseWords(string s) { stack<string> Stack; if(s.length()==1) return s; string temp; for(int i=0;i<=s.length();i++){ if(i==s.length() || s[i]==' '){ if(temp=="") continue; Stack.empty()?Stack.push(temp):Stack.push(temp+" "); temp=""; } else{ temp+=s[i]; //if(i==s.length()-1) Stack.empty()?Stack.push(temp):Stack.push(temp+" "); } } string res; while(!Stack.empty()){ res += Stack.top(); Stack.pop(); } return res; } }; ``` Space complexity: $O(n)$ Problem: To reduce space to $O(1)$ Sol: reverse the words one by one and reverse the whole string Example: 1. the sky is blue 2. eht yks si eulb (reverse the words one by one) 3. blue is the sky (reverse the whole string) The code is hard to understand: - i will skip whitespace and find words, then copy the words to j - j will add space between words and copy the words from i - l is the pos of the start of the word that need to reverse Space complexity: $O(1)$ ```cpp class Solution { public: // function to reverse any part of string from i to j (just one word or entire string) void reverseword(string &s, int i, int j){ while(i<j){ char t=s[i]; s[i++]=s[j]; s[j--]=t; } } string reverseWords(string &s) { int i=0, j=0; // j records the pos of redundant white space int l=0; // l is the start of redundant white space int len=s.length(); int wordcount=0; // count how many words are done while(true){ while(i<len && s[i] == ' ') i++; // skip spaces in front of the word if(i==len) break; if(wordcount) s[j++]=' '; // add space between words only wordcount!=0 needs space l=j; while(i<len && s[i] != ' ') {s[j]=s[i]; j++; i++;} // move the words to redundant space reverseword(s,l,j-1); // reverse word in place wordcount++; } s.resize(j); // resize result string reverseword(s,0,j-1); // reverse whole string return s; } }; ``` ### One edit distance ![](https://i.imgur.com/xa0KnXc.png) Three situations 1. s.len()==t.len() -> change one char 2. s.len()==t.len()+1 -> delete one char 3. s.len()==t.len()-1 -> insert one char If we swap s with t when t.len is bigger, than we can simplify to two cases, delete and change. ```cpp class Solution { public: bool isOneEditDistance(string s, string t) { if (s.size() < t.size()) swap(s, t); int m = s.size(), n = t.size(), diff = m - n; if (diff >= 2) return false; else if (diff == 1) { // we swap(s,t) so delete or insert -> delete for (int i = 0; i < n; ++i) { if (s[i] != t[i]) { return s.substr(i + 1) == t.substr(i); } } return true; } else { // change cnt can only be 1 int cnt = 0; for (int i = 0; i < m; ++i) { if (s[i] != t[i]) ++cnt; } return cnt == 1; } } }; ``` ### Rearrange String k Distance apart ==Hard== ![](https://i.imgur.com/5k59KAa.png) ![](https://i.imgur.com/MsFD117.png) ```Initial``` 先記錄string裡每個char的個數,從第0位開始排,由個數多(較容易靠近的)的先排。寫一個function用來檢查前k位內的char種類,以避免error。 ```Sol``` char個數->map 個數多->max heap ```improvements``` 原先檢查k位內是否重複的function被改良,改為每次只選 min(k,len) 個 char,選完之後此 char 先不 push 進 heap 中,而是先push進 temp vector,等到此輪結束之後,再將其push進heap。 為何如此就可以保證k內不會重複? 假設x在兩輪中會在k內,表示第一輪x所在的位置x1,與第二輪位置x2的距離小於k,代表之間有一個char沒有再被push進heap中。然而,heap是依照frequency來選的,代表前方的char's frequency必定>=x's frequency,因此不可能發生。 ```cpp class Solution { public: string rearrangeString(string str, int k) { if (k == 0) return str; string res; int len = (int)str.size(); unordered_map<char, int> m; // map for (auto a : str) ++m[a]; priority_queue<pair<int, char>> q; // heap for (auto it = m.begin(); it != m.end(); ++it) { q.push({it->second, it->first}); // map <char, int> // map->second is int // map->first is char } while (!q.empty()) { vector<pair<int, int>> v;// temp vector to record char that needed to be push in the heap in next round int cnt = min(k, len); for (int i = 0; i < cnt; ++i) { if (q.empty()) return ""; /*將top pop出來,並將其count-1,若count-1>0,再下一輪中將其push進heap中 * */ auto t = q.top(); q.pop(); res.push_back(t.second); if (--t.first > 0) v.push_back(t); --len; } for (auto a : v) q.push(a);//push in the heap in next round } return res; } }; ``` ### Read N Characters Given Read4 II - Call multiple times ![](https://i.imgur.com/UUEbmYU.png) ![](https://i.imgur.com/6XLBXnc.png) Special question to use an api Not to difficult ```cpp // Forward declaration of the read4 API. int read4(char *buf); class Solution { public: int read(char *buf, int n) { for(int i=0;i<n;i++){ if(readPos==writePos){ writePos = read4(buff); readPos = 0; if(!writePos) return i; } buf[i] = buff[readPos++]; } } private: int readPos = 0 ;// the pos that we read from buff int writePos = 0; // the pos we can get from the string char buff[4]; }; ``` ## Anagram ### Valid Anagram ![](https://i.imgur.com/XOFXMHa.png) 1. 分別對兩個輸入參數跑回圈並且丟進相對應的ARRAY裡面 2. input兩個參數一個分別做紀錄+ +,另一個做紀錄 - - 3. 最後在檢查一次集合裡面是不是有大於0的值 ```cpp class Solution { public: bool isAnagram(string s, string t) { // 長度一定要一樣才有可能配對 if (s.length() != t.length()) return false; int all[26]; for(int i=0;i<26;i++){ all[i] = 0; } for (int i = 0; i < s.length(); i++) { // 如果都能夠組成相對應的那字母數量應該會是一致的 all[s[i] - 'a']++; all[t[i] - 'a']--; } for (int i : all) { if (i != 0) return false; } return true; } }; ``` ### Anagram Difference ![](https://i.imgur.com/uRcedj8.png) ![](https://i.imgur.com/GWEHdVx.png) 1. 分別對兩個輸入參數跑回圈並且丟進相對應的ARRAY裡面 2. input兩個參數一個分別做紀錄+ +,另一個做紀錄 - - 3. 分別記錄>0的個數(s1需改變的個數)與<0的個數(s2需改變的個數) 4. 若個數大於字串長度即無法使兩者anagram 5. else: ans = (+個數 + -個數)/2 ```cpp // C++ Program to find minimum number // of manipulations required to make // two strings identical #include <bits/stdc++.h> using namespace std; // Counts the no of manipulations // required int countManipulations(string s1, string s2) { int count = 0; // store the count of character int char_count[26]; for (int i = 0; i < 26; i++) { char_count[i] = 0; } // iterate though the first String // and update count for (int i = 0; i < s1.length(); i++) char_count[s1[i] - 'a']++; // iterate through the second string // update char_count. // if character is not found in // char_count then increase count for (int i = 0; i < s2.length(); i++) { char_count[s2[i] - 'a']--; } for(int i = 0; i < 26; ++i) { if(char_count[i] != 0) { count+=abs(char_count[i]); } } return count; } // Driver code int main() { string s1 = "ddcf"; string s2 = "cedk"; cout<<countManipulations(s1, s2); } // This code is contributed by vt_m. ``` ### Group anagram ![](https://i.imgur.com/vsVXnnP.png) 1. first sort the string 2. check whether the map has the string, map is the string position of the res 3. if not : res + one vector 4. add the string to res ```cpp class Solution { public: string shortestPalindrome(string s) { string r = s; reverse(r.begin(), r.end()); string t = s + "#" + r; vector<int> next(t.size(), 0); int j=0, i=1; while(i<t.size() && j<t.size()){ if(t[i]==t[j]){ next[i] = j+1; i++; j++; continue; } else if(j!=0){ j = next[j-1]; continue; } else{ i++; continue; } } return r.substr(0, s.size() - next.back()) + s; } }; ``` ## Palindrome ### Palindrome Partitioning ![](https://i.imgur.com/wbAl0EU.png) problem : slow solution: dynamic programming to optimize ```cpp class Solution { public: vector<vector<string>> partition(string s) { vector<vector<string>> res; if(s.size()==1){ vector<string> temp_res={s}; res.push_back(temp_res); return res; } for(int len=1;len<=s.size();len++){ string temp = s.substr(0,len); if(ispalindrome(temp)){ vector<string> temp_res={temp}; if(len==s.size()){ res.push_back(temp_res); continue; } for(auto v: partition(s.substr(len))){ temp_res={temp}; for(auto sub_v: v){ temp_res.push_back(sub_v); } res.push_back(temp_res); //v.insert(v.begin(), temp);// insert is slow //res.push_back(v); } } else continue; } return res; } bool ispalindrome(string s){ for(int i=0;i<=(s.size()-1)/2;i++){ if(s[i]==s[s.size()-i-1]) continue; else return false; } return true; } }; ``` ```cpp class Solution { public: vector<vector<string>> partition(string s) { vector<vector<string>> res; vector<string> out; helper(s, 0, out, res); return res; } void helper(string s, int start, vector<string>& out, vector<vector<string>>& res) { if (start == s.size()) { res.push_back(out); return; } for (int i = start; i < s.size(); ++i) { if (!isPalindrome(s, start, i)) continue; out.push_back(s.substr(start, i - start + 1)); // push in the vector helper(s, i + 1, out, res);// recursively do the rest out.pop_back();// pop out already push in the res } } bool isPalindrome(string s, int start, int end) { while (start < end) { if (s[start] != s[end]) return false; ++start; --end; } return true; } }; ``` DP,詳細說明在 Palindrome substring題目中 ```cpp class Solution { public: vector<vector<string>> partition(string s) { int n = s.size(); vector<vector<string>> res; vector<string> out; vector<vector<bool>> dp(n, vector<bool>(n)); for (int i = 0; i < n; ++i) { for (int j = 0; j <= i; ++j) { if (s[i] == s[j] && (i - j <= 2 || dp[j + 1][i - 1])) { dp[j][i] = true; } } } helper(s, 0, dp, out, res); return res; } void helper(string s, int start, vector<vector<bool>>& dp, vector<string>& out, vector<vector<string>>& res) { if (start == s.size()) { res.push_back(out); return; } for (int i = start; i < s.size(); ++i) { if (!dp[start][i]) continue; out.push_back(s.substr(start, i - start + 1)); helper(s, i + 1, dp, out, res); out.pop_back(); } } }; ``` ### Palindrome substring easy ![](https://i.imgur.com/bSVt97B.png) ![](https://i.imgur.com/KGQjHZQ.png) dp[i][j]: 表string i~j是否為palindrome dp[i][j] is true if s[i] == s[j] && dp[i + 1][j - 1] is true ```cpp class Solution { public: int countSubstrings(string s) { int n = s.size(), res = 0; vector<vector<bool>> dp(n, vector<bool>(n)); for (int i = n - 1; i >= 0; --i) { for (int j = i; j < n; ++j) { dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i + 1][j - 1]); if (dp[i][j]) ++res; } } return res; } }; ``` ### Implement strStr() ![](https://i.imgur.com/9iSognr.png) KMP implementation ```cpp class Solution { public: int strStr(string haystack, string needle) { // KMP table vector <int> KMP(needle.length(),0); int i=0, j=1; while(j<needle.length()){ if(needle[i] == needle[j]){//相等 KMP[j] = i+1; i++; j++; continue; } else if(i!=0){//不等且i不為0 i = KMP[i-1];//最困難的部分 continue; } else{//不等但i為0 j++; } } // comparison i=0; j=0; while(j<needle.length() && i<haystack.length()){ if(needle[j]==haystack[i]){ i++; j++; continue; } else{ if(j==0){ i++; continue; } else{ j = KMP[j-1]; continue; } } } if(j==needle.length()){ return i-j; } else{ return -1; } } }; ``` ### Shortest Palindromic Substring ==Hard== ![](https://i.imgur.com/uOf81xT.png) 首先將待處理的字符串s翻轉得到t,然後比較原字符串s和翻轉字符串t,從第一個字符開始逐一比較,如果相等,說明s本身就是回文串,不用添加任何字符,直接返回即可;如果不相等,s去掉最後一位,t去掉第一位,繼續比較,以此類推直至有相等,或者循環結束,這樣就能將==兩個字符串在正確的位置拼接起來了==,但這種寫法卻會超時 TLE,無法通過 OJ。 因此,使用KMP來解決 先分析字串組成,若只能從頭開始加字母,則發現字串必由一個==由0起始的回文(包含一個字母)+一串不回文所組成==,簡寫為 P + NP 例如 babad = baba + d aacecaaa = aacecaa + a aaacecaa = aaa + cecaa 將字串s與反字串t相接=> P+NP+NP+P (注意:中間需要多加一個符號作為區隔) 若使用KMP,則可在最後一個位置的table上找到P的長度,並將原字串簡掉P留下NP並加到字串前方 ```cpp class Solution { public: string shortestPalindrome(string s) { string r = s; reverse(r.begin(), r.end()); string t = s + "#" + r; vector<int> next(t.size(), 0); for (int i = 1; i < t.size(); ++i) { int j = next[i - 1]; while (j > 0 && t[i] != t[j]) j = next[j - 1]; next[i] = (j += t[i] == t[j]); } return r.substr(0, s.size() - next.back()) + s; } }; ``` ### Longest Palindromic Substring ![](https://i.imgur.com/obMxGO1.png) #### Method 1 選擇一個中心點並向外擴張 (要注意奇偶有不同形式的對稱,所以要做兩次) -> O(N^2) ```cpp class Solution { public: string longestPalindrome(string s) { if (s.size() < 2) return s; int n = s.size(), maxLen = 0, start = 0; for (int i = 0; i < n - 1; ++i) { searchPalindrome(s, i, i, start, maxLen); searchPalindrome(s, i, i + 1, start, maxLen); } return s.substr(start, maxLen); } void searchPalindrome(string s, int left, int right, int& start, int& maxLen) { while (left >= 0 && right < s.size() && s[left] == s[right]) { --left; ++right; } if (maxLen < right - left - 1) { start = left + 1; maxLen = right - left - 1; } } }; ``` #### Method 2 觀察noon,nooon 可以發現若我們使用兩個指標 left, right,且right必須跳過重複的字母,odds or even can be done in the same way. ```cpp class Solution { public: string longestPalindrome(string s) { if (s.size() < 2) return s; int n = s.size(), maxLen = 0, start = 0; for (int i = 0; i < n;) { if (n - i <= maxLen / 2) break; int left = i, right = i; while (right < n - 1 && s[right + 1] == s[right]) ++right; i = right + 1; while (right < n - 1 && left > 0 && s[right + 1] == s[left - 1]) { ++right; --left; } if (maxLen < right - left + 1) { maxLen = right - left + 1; start = left; } } return s.substr(start, maxLen); } }; ``` #### Method 3 DP 此题还可以用动态规划 Dynamic Programming 来解,根 Palindrome Partitioning II 的解法很类似,我们维护一个二维数组 dp,其中 dp[i][j] 表示字符串区间 [i, j] 是否为回文串,当 i = j 时,只有一个字符,肯定是回文串,如果 i = j + 1,说明是相邻字符,此时需要判断 s[i] 是否等于 s[j],如果i和j不相邻,即 i - j >= 2 时,除了判断 s[i] 和 s[j] 相等之外,dp[i + 1][j - 1] 若为真,就是回文串,通过以上分析,可以写出递推式如下: ``` dp[i][j] = 1 if i=j or if j=i+1 && s[i]==s[j] or s[i]=s[j] && dp[i+1][j-1] ==1 ``` ```cpp class Solution { public: string longestPalindrome(string s) { if (s.empty()) return ""; int n = s.size(), dp[n][n] = {0}, left = 0, len = 1; for (int i = 0; i < n; ++i) { dp[i][i] = 1; for (int j = 0; j < i; ++j) { dp[j][i] = (s[i] == s[j] && (i - j < 2 || dp[j + 1][i - 1])); if (dp[j][i] && len < i - j + 1) { len = i - j + 1; left = j; } } } return s.substr(left, len); } }; ``` ## Substring ### Longest Substring Without Repeating Characters ![](https://i.imgur.com/jELM3Iy.png) 如果给一个例子中的例子 "abcabcbb",让你手动找无重复字符的子串,该怎么找。博主会一个字符一个字符的遍历,比如 a,b,c,然后又出现了一个a,那么此时就应该去掉第一次出现的a,然后继续往后,又出现了一个b,则应该去掉一次出现的b,以此类推,最终发现最长的长度为3。 ```cpp class Solution { public: int lengthOfLongestSubstring(string s) { // res = the max substring so far // left = the start point of the max substring int res = 0, left = 0, n = s.size(); unordered_map<int, int> m; for (int i = 0; i < n; ++i) { if (m.count(s[i]) && m[s[i]] >= left) { // map.count 即檢查是否有存入值 // m[s[i]] >= left 檢查上一個s[i]是否 in max substring之中 left = m[s[i]] + 1; } m[s[i]] = i; res = max(res, i - left + 1); } return res; } }; ``` ### Longest substring with at most K distinct characters ![](https://i.imgur.com/eFli46W.png) 1. map 用來記錄現在的longest string 各有幾個字母 2. map.size() > k時,代表超過k個distinct char 3. 所以從left(substring 的 start) 開始一個一個減直到 map.size() < k 4. 比較 res , i-left+1 的大小 ```cpp class Solution { public: int lengthOfLongestSubstringKDistinct(string s, int k) { int res = 0, left = 0; unordered_map<char, int> m; for (int i = 0; i < s.size(); ++i) { ++m[s[i]]; while (m.size() > k) { if (--m[s[left]] == 0) m.erase(s[left]); ++left; } res = max(res, i - left + 1); } return res; } }; ``` ### ==Minimum Window Substring== ![](https://i.imgur.com/23PjXFI.png) - 先扫描一遍T,把对应的字符及其出现的次数存到 HashMap 中。 - 然后开始遍历S,就把遍历到的字母对应的 HashMap 中的 value 减一,如果减1后仍大于等于0,cnt 自增1。 - 如果 cnt 等于T串长度时,开始循环,纪录一个字串并更新最小字串值。然后将子窗口的左边界向右移,如果某个移除掉的字母是T串中不可缺少的字母,那么 cnt 自减1,表示此时T串并没有完全匹配。 ```cpp class Solution { public: string minWindow(string s, string t) { string res = ""; unordered_map<char, int> letterCnt; int left = 0, cnt = 0, minLen = INT_MAX; // left 左指標 // cnt substring有幾個t的char // minLen = min len so far for (char c : t) ++letterCnt[c]; for (int i = 0; i < s.size(); ++i) { if (--letterCnt[s[i]] >= 0) ++cnt; while (cnt == t.size()) { // substring已經包含t if (minLen > i - left + 1) { minLen = i - left + 1; res = s.substr(left, minLen); } if (++letterCnt[s[left]] > 0) --cnt; // 表left減去了一個t中的char ++left; } } return res; } }; ``` ## Parentheses ### Generate Parentheses ![](https://i.imgur.com/yeeSydL.png) #### Method 1 generate all the string then delete the wrong ones ```cpp // To generate all possible string void generateStr(string cur, int n, vector<string>& vsResult) { if (n == 0) { if (isValid(cur)) { vsResult.push_back(cur); } } else { generateStr(cur+"(", n - 1, vsResult); generateStr(cur+")", n - 1, vsResult); } } ``` #### Method 2 假設我知道有n組()要排列,**而每一組的"("必須要比")"還要先出現**,那我們就可以假設open為還沒放入的"("的數量,close為還沒放入的")"數量: void generateStr(string combination, int open, int close) 1. Initiale : open一開始就是n,close為0 2. 當每排入一個open,就少一個open要排入,但多一個close排入;當排入一個close,就少一個close可以排入,open的數量不變 3. 最後當open和close都為0的時候,就表示排完了 ```cpp class Solution { public: vector<string> generateParenthesis(int n) { vector<string> res; addingpar(res, "", n, 0); return res; } void addingpar(vector<string> &v, string str, int open, int close){ if(open==0 && close==0) { v.push_back(str); return; } if(close > 0){ addingpar(v, str+")", open, close-1); } // 會繼續做下去 因此可以產生多個不同結果 if(open > 0){ addingpar(v, str+"(", open-1, close+1); } } }; ``` <br><br> ### Longest Valid Parentheses ![](https://i.imgur.com/vJrVMhL.png) set a stack, push the position when we met '(', and pop the position from the stack when we met ')' ```cpp class Solution { public: int longestValidParentheses(string s) { int res = 0, start = 0, n = s.size(); stack<int> st; for (int i = 0; i < n; ++i) { if (s[i] == '(') st.push(i); else if (s[i] == ')') { if (st.empty()) start = i + 1; else { st.pop(); res = st.empty() ? max(res, i - start + 1) : max(res, i - st.top()); // 若pop完st為空,長度即為 i-start+1,去跟res比大小 // 不為空,長度等於 i-stack.top() } } } return res; } }; ``` #### 求極值: DP OR GREADY : DP to optimize the memory 1.状态: DP[i]:以s[i-1]为结尾的longest valid parentheses substring的长度。 2.通项公式: s[i] = '(':DP[i] = 0 s[i] = ')':找i前一个字符的最长括号串DP[i]的前一个字符j = i-2-DP[i-1] DP[i] = DP[i-1] + 2 + DP[j],如果j >=0,且s[j] = '(' DP[i] = 0,如果j<0,或s[j] = ')' ......... ( x x x x ) j i-2 i-1 ![](https://i.imgur.com/PvzXf3r.png) 证明:不存在j' < j,且s[j' : i]为valid parentheses substring。 假如存在这样的j',则s[j'+1 : i-1]也valid。那么对于i-1来说: ( x x x x x j' j'+1 i-1 这种情况下,i-1是不可能有比S[j'+1 : i-1]更长的valid parentheses substring的。 3.计算方向 显然自左向右,且DP[0] = 0 ```cpp class Solution { public: int longestValidParentheses(string s) { int n = s.size(), maxLen = 0; vector<int> dp(n+1,0); for(int i=1; i<=n; i++) { int j = i-2-dp[i-1]; if(s[i-1]=='(' || j<0 || s[j]==')') dp[i] = 0; else { dp[i] = dp[i-1]+2+dp[j]; maxLen = max(maxLen, dp[i]); } } return maxLen; } }; ``` <br><br> ### Different Ways to Add Parentheses #### ==Learn Divide and conquer== 1. divide problem into small problem and make sure they can obtain the same rule 2. conqure (recursively sovle the small problem) 3. merge small problem into the final answer [reference](https://medium.com/brandons-computer-science-notes/divide-and-conquer-algorithms-4e83d9999ffa) ![](https://i.imgur.com/dykZ5IQ.png) ![](https://i.imgur.com/OqkVYrS.png) ```cpp class Solution { public: vector<int> diffWaysToCompute(string input) { vector<int> res; for (int i = 0; i < input.size(); ++i) { if (input[i] == '+' || input[i] == '-' || input[i] == '*') { vector<int> left = diffWaysToCompute(input.substr(0, i)); vector<int> right = diffWaysToCompute(input.substr(i + 1)); for (int j = 0; j < left.size(); ++j) { for (int k = 0; k < right.size(); ++k) { if (input[i] == '+') res.push_back(left[j] + right[k]); else if (input[i] == '-') res.push_back(left[j] - right[k]); else res.push_back(left[j] * right[k]); } } } } if (res.empty()) res.push_back(stoi(input)); // input 僅一位數字 return res; } }; ``` ### Remove Invalid Parentheses ![](https://i.imgur.com/2KkX8mE.png) 這題很出乎意料的是沒有特別的解法,幾乎就是和暴力法一樣,只是中間部分優化可以不用走過每個情形。 暴力法: 每一個 ‘(’ 和 ’)’ 都可以選擇移除或者保留,相當於找到所有的子集 (字母部分不重要),從裡面中找到合法的以及移除 ‘(’ 或 ’)’ 最少的就是答案了。 找過所有的子集這一部分用遞迴 DFS 就比較簡單了。 (to be continued ...)