745. Prefix and Suffix Search

745. Prefix and Suffix Search

Question

Design a special dictionary with some words that searchs the words in it by a prefix and a suffix.

Implement the WordFilter class:

  • WordFilter(string[] words) Initializes the object with the words in the dictionary.
  • f(string prefix, string suffix) Returns the index of the word in the dictionary, which has the prefix prefix and the suffix suffix. If there is more than one valid index, return the largest of them. If there is no such word in the dictionary, return -1.

Solution

改版的前缀树,用后缀+符号+前缀的形式记录字典树。

TrieNode

用参数id记录TrieNode经历过的最大下标。

27位TrieNode数组children[]记录子TrieNode。

初始化

将words[]中的每个字符串记录在TrieNode上,保存TrieNode的根节点root。
在遍历每个字符串时,需要将每个“后缀+符号+前缀”组成的新字符串记录在字典树中,并更新当前节点的id值。

将后缀部分添加完毕后,加入特殊符号。检查并创建当前节点的curr[26]位置。
然后继续将整个前缀字符串加入字典树,并更新当前节点的id值。

搜索

搜索时,先遍历后缀,查找字典树的路径是否存在,并更新当前节点的位置,如不存在则返回-1。
然后检查并跳过curr.children[26]的位置,如不存在则返回-1。
最后遍历前缀,更新节点位置,如果路径不存在则返回-1。

最后返回当前节点的id即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class WordFilter {
class TrieNode{
int id;
TrieNode[] children;
public TrieNode(){
children = new TrieNode[27];
id = 0;
}
}

TrieNode root;
public WordFilter(String[] words) {
root = new TrieNode();

for(int k = 0; k < words.length; k++){
String word = words[k];
for(int i = 0; i < word.length(); i++){
TrieNode curr = root;
int j = i;
while(j < word.length()){
int index = word.charAt(j) - 'a';
if(curr.children[index] == null) curr.children[index] = new TrieNode();
curr = curr.children[index];
curr.id = k;
j++;
}
if(curr.children[26] == null) curr.children[26] = new TrieNode();
curr = curr.children[26];
curr.id = k;
for(char c : word.toCharArray()){
int index = c - 'a';
if(curr.children[index] == null) curr.children[index] = new TrieNode();
curr = curr.children[index];
curr.id = k;
}
}
}
}

public int f(String prefix, String suffix) {
TrieNode curr = root;
for(char c : suffix.toCharArray()){
int index = c - 'a';
if(curr.children[index] == null) return -1;
curr = curr.children[index];
}

if(curr.children[26] == null) return -1;
curr = curr.children[26];

for(char c : prefix.toCharArray()){
int index = c - 'a';
if(curr.children[index] == null) return -1;
curr = curr.children[index];
}
return curr.id;
}
}

/**
* Your WordFilter object will be instantiated and called as such:
* WordFilter obj = new WordFilter(words);
* int param_1 = obj.f(prefix,suffix);
*/
208. Implement Trie (Prefix Tree)

208. Implement Trie (Prefix Tree)

Question

A trie (pronounced as “try”) or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker.

Implement the Trie class:

  • Trie() Initializes the trie object.

  • void insert(String word) Inserts the string word into the trie.

  • boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise.

  • boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.

Solution

参考:实现 Trie :「二维数组」&「TrieNode」方式

前缀树,字典树结构实现。

前缀树 Trie

前缀树结构用“边”记录有无字符,用“点”记录单词结尾以及后续的字符串字符。
root节点记录字典的根节点。

TrieNode节点

用TrieNode节点实现前缀树。
真值end记录是否为字符串的结尾,创建时默认为false。
数组TrieNode[] children记录子节点。

insert()方法

从根节点root开始,遍历字符串。
如果对应的子节点位置为空,则创建新的TrieNode节点。
向下移动当前节点。

将最后一个节点设置为end,使得整个字符串被字典记录。

search()方法

从根节点root开始,遍历字符串。
如果子节点位置为空,则这个字符串不在字典中,返回false。
否则向下移动当前节点。

遍历完毕,如果当前节点的end为true,则存在该字符串,返回true。

startsWith()方法

从根节点root开始,遍历前缀字符串。
如果子节点位置为空,则这个前缀不在字典中,返回false。
否则向下移动当前节点。

最后返回true。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Trie {
class TrieNode {
boolean end;
TrieNode[] children = new TrieNode[26];
}
TrieNode root;
public Trie() {
root = new TrieNode();
}

public void insert(String word) {
TrieNode curr = root;
for(char c : word.toCharArray()){
int i = c - 'a';
if(curr.children[i] == null) curr.children[i] = new TrieNode();
curr = curr.children[i];
}
curr.end = true;
}

public boolean search(String word) {
TrieNode curr = root;
for(char c : word.toCharArray()){
int i = c - 'a';
if(curr.children[i] == null) return false;
curr = curr.children[i];
}
return curr.end;
}

public boolean startsWith(String prefix) {
TrieNode curr = root;
for(char c : prefix.toCharArray()){
int i = c - 'a';
if(curr.children[i] == null) return false;
curr = curr.children[i];
}
return true;
}


}

/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/
1048. Longest String Chain

1048. Longest String Chain

Question

You are given an array of words where each word consists of lowercase English letters.

word<sub>A</sub> is a predecessor of word<sub>B</sub> if and only if we can insert exactly one letter anywhere in word<sub>A</sub> without changing the order of the other characters to make it equal to word<sub>B</sub>.

  • For example, "abc" is a predecessor of "ab<u>a</u>c", while "cba" is not a predecessor of "bcad".

A word chain* *is a sequence of words [word<sub>1</sub>, word<sub>2</sub>, ..., word<sub>k</sub>] with k >= 1, where word<sub>1</sub> is a predecessor of word<sub>2</sub>, word<sub>2</sub> is a predecessor of word<sub>3</sub>, and so on. A single word is trivially a word chain with k == 1.

Return *the length of the longest possible word chain with words chosen from the given list of *words.

Solution

排序+动态规划。
采用一个一维动态规划数组记录到某个word的最大字符串链长度。

排序

每个predecessor必定比next少1,因此首先将数组根据word的长度排序。

动态规划

数组dp[i]记录到i位置时的最大长度。
双重遍历i和j,其中j>i。如果words[i-1]是words[j-1]的predecessor,则更新dp[j]为dp[j]与dp[i]+1的较大值。

辅助方法 isValid()

判断predecessor是否有效。
初始化一个flag为false记录不同字符是否已出现。
如果两者长度差不为1直接返回false。
双指针分别指向两个字符串,如果两个当前字符相等,则两个指针共同前进。
如果不相等且flag为true则i指针前进,并且将flag改为false。否则返回false。
如果循环结束返回true。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public int longestStrChain(String[] words) {
int max = 0;
Arrays.sort(words, (a,b) -> a.length() - b.length());
int[] dp = new int[words.length+1];
for(int i = 1; i <= words.length; i++){
for(int j = i + 1; j <= words.length; j++){
if(isValid(words[i-1], words[j-1])){
dp[j] = Math.max(dp[j], dp[i] + 1);
max = Math.max(max, dp[j]);
}
}
}
return max + 1;
}

private boolean isValid(String predecessor, String next){
if(predecessor.length() + 1 != next.length()) return false;
boolean flag = true;
int i = 0, j = 0;
while(i < next.length() && j < predecessor.length()){
if(next.charAt(i) == predecessor.charAt(j)){
i++;
j++;
}
else if(flag){
i++;
flag = false;
}
else{
return false;
}
}
return true;
}
}

2299. Strong Password Checker II

Question

A password is said to be strong if it satisfies all the following criteria:

  • It has at least 8 characters.

  • It contains at least one lowercase letter.

  • It contains at least one uppercase letter.

  • It contains at least one digit.

  • It contains at least one special character. The special characters are the characters in the following string: "!@#$%^&*()-+".

  • It does not contain 2 of the same character in adjacent positions (i.e., "aab" violates this condition, but "aba" does not).

Given a string password, return true* if it is a strong password*. Otherwise, return false.

Solution

直接遍历,记录四个真值对应四个符号,初始化为false,和上一个字符last。
每次遍历检查当前字符,如果等于上个字符则直接返回false。
根据字符范围来改变四个真值,最后返回四个真值的和运算。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean strongPasswordCheckerII(String password) {
if(password.length() < 8) return false;
char last = ' ';
boolean hasLower = false, hasUpper = false, hasDigit = false, hasSpecial = false;
char[] word = password.toCharArray();

for(int i = 0; i < password.length(); i++){
char cur = word[i];
if(last == cur) return false;
if(cur >= 'a' && cur <= 'z') hasLower = true;
else if(cur >= 'A' && cur <= 'Z') hasUpper = true;
else if(cur >= '0' && cur <= '9') hasDigit = true;
else hasSpecial = true;
last = cur;
}

return hasLower && hasUpper && hasDigit && hasSpecial;
}
}
1332. Remove Palindromic Subsequences

1332. Remove Palindromic Subsequences

Question

You are given a string s consisting only of letters 'a' and 'b'. In a single step you can remove one palindromic subsequence from s.

Return the minimum number of steps to make the given string empty.

A string is a subsequence of a given string if it is generated by deleting some characters of a given string without changing its order. Note that a subsequence does not necessarily need to be contiguous.

A string is called palindrome if is one that reads the same backward as well as forward.

Solution

当字符为空时,返回0。
注意本题中的子序列不需要连续。
由于只有’a’与’b’两个字符,因此最多两步即可将字符串清空。

辅助方法判断字符串是否为回文字符串。
如果为真则返回1,只需要删除一次。

如果为假则返回2,需要先删除所有的’a’,再删除所有的’b’。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int removePalindromeSub(String s) {
if(s.length() == 0) return 0;
return isPalindrome(s) ? 1 : 2;
}

private boolean isPalindrome(String s){
int i = 0, j = s.length() - 1;
while(j > i){
if(s.charAt(i) != s.charAt(j)) return false;
i++;
j--;
}
return true;
}
}
318. Maximum Product of Word Lengths

318. Maximum Product of Word Lengths

Question

Given a string array words, return the maximum value of length(word[i]) * length(word[j]) where the two words do not share common letters. If no such two words exist, return 0.

Solution

正常的思路可能是用哈希表保存字符串中出现过的字符。此时表的大小是26也是一个常数。

我们可以采用位运算进行状态压缩,通过一个整数的二进制位保存二十六个字符的存在状态。

状态压缩

我们采用一个整数数组bin[]储存状态压缩后的整数。

遍历每个字符串,在字符串中遍历每一个字符。
初始化整数bit为0。在遍历字符时计算当前字符与字符’a’距离的差n,然后将1左移n位,填入(或运算)整数bit中。

比较

遍历words[]中的两个字符串,当bin[i]与bin[j]进行与运算结果为0时,则两个字符串没有共同字符。
此时更新二者长度乘积的最大值max。

最后返回max即可。

时间复杂度:O(L + n2)。L为所有字符的总长度。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int maxProduct(String[] words) {

int max = 0;
int bin[] = new int[words.length];

for(int i = 0; i < words.length; i++){
int bit = 0;
for(int j = 0; j < words[i].length(); j++){
bit |= (1 << (words[i].charAt(j) - 'a'));
}
bin[i] = bit;
}

for(int i = 0; i < words.length; i++){
for(int j = i+1; j < words.length; j++){
if((bin[i] & bin[j]) == 0) max = Math.max(max, words[i].length() * words[j].length());
}
}
return max;
}
}
32. Longest Valid Parentheses

32. Longest Valid Parentheses

Question

Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.

Solution

如果有右括号先出现于左括号,则其永远无法配对。
而如果是左括号先出现,则其有可能可以和之后的右括号进行配对。

因此我们可以将左括号的位置入栈,等待配对。
并记录上一个不能配对的右括号的位置。以此来确定当前位置的有效括号长度。

首先将上一个不能组队的右括号lastRightParenthese初始化为-1。
初始化一个最大有效长度best。

然后遍历字符串:

  • 当遇到左括号时:
    • 将当前下标i压入栈。(记录等待配对的左括号)
  • 遇到右括号时:
    • 如果栈为空,则更新上一个不能组队的右括号的位置为当前下标i。(此时出现的右括号一定无法配对)
    • 如果栈不为空,则挤出上一个左括号的下标,与右括号配对。
      • 挤出上一个左括号后,如果栈为空。则将当前的有效括号总长度为i - lastRightParenthese。(当前的右括号和上一个不能配对的右括号的差)
      • 否则当前的有效括号总长度为i - stack.peek()。(当前的右括号和上一个不配对的左括号的差。)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public int longestValidParentheses(String s) {
Deque<Integer> stack = new LinkedList<>();
int best = 0;
int lastRightParenthese = -1; //上一个不能配对的右括号位置,初始化为-1
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) == '('){ //如果当前位置是左括号,则将下标入栈
stack.push(i);
}
else{ //如果当前位置是右括号...
if(stack.isEmpty()){ //如果没有可以配对的左括号,则更新不能配对的右括号的位置
lastRightParenthese = i;
}
else{
stack.pop(); //如果有可以配对的右括号,则从栈中挤出
if(stack.isEmpty()){ //挤出后如果没有剩余的左括号了,则当前有效括号长度为当前位置减去上一个无法配对的右括号的位置
best = Math.max(best, i - lastRightParenthese);
}
else{ //如果挤出后栈内仍有剩余的左括号存在,则当前有效括号长度为当前位置减去栈顶的左括号的位置
best = Math.max(best, i - stack.peek());
}
}
}
}
return best;
}
}

2278. Percentage of Letter in String

Question

Given a string s and a character letter, return* the percentage of characters in s that equal letter rounded down to the nearest whole percent.*

Solution

一次遍历,计算字符出现的次数。
返回字符出现的次数除以字符长度乘以100。

Code

1
2
3
4
5
6
7
8
9
10
11
class Solution {
public int percentageLetter(String s, char letter) {
int count = 0;
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) == letter){
count++;
}
}
return count * 100 / s.length();
}
}

2273. Find Resultant Array After Removing Anagrams

Problem

You are given a 0-indexed string array words, where words[i] consists of lowercase English letters.

In one operation, select any index i such that 0 < i < words.length and words[i - 1] and words[i] are anagrams, and delete words[i] from words. Keep performing this operation as long as you can select an index that satisfies the conditions.

Return words after performing all operations. It can be shown that selecting the indices for each operation in any arbitrary order will lead to the same result.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase using all the original letters exactly once. For example, "dacb" is an anagram of "abdc".

Solution

由于只用查看上一个元素。
记录一个prev[]数组记录上一个元素的字符情况。
遍历words,用数组bin[]统计每个字母出现的次数,同时减去prev数组的统计数字。
如果数组中的每个统计结果都为0,则是上一个字符串的Anagram。
否则是一个新的字符串,将其加入结果。
然后将上一个数组prev更新为当前的统计数组bin[]。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public List<String> removeAnagrams(String[] words) {
int[] prev = new int[26];

List<String> ans = new ArrayList<>();

for(String word : words){
int[] bin = new int[26];
char[] chars = word.toCharArray();
for(int i = 0; i < chars.length; i++){
prev[chars[i]-'a']--;
bin[chars[i]-'a']++;
}
if(!allZero(prev)){
ans.add(word);
}
prev = bin;
}
return ans;
}

private boolean allZero(int[] bin){
for(int num : bin){
if(num != 0) return false;
}
return true;
}
}
1209. Remove All Adjacent Duplicates in String II

1209. Remove All Adjacent Duplicates in String II

Question

You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters from s and removing them, causing the left and the right side of the deleted substring to concatenate together.

We repeatedly make k duplicate removals on s until we no longer can.

Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique.

Solution

用一个栈保存当前的字符,同时建立一个cnt[]数组记录当前字符连续出现的次数。

遍历字符串。

如果当前的字符与栈顶的字符相同时,获取数组中记录的当前连续次数lastCount并加一。
如果lastCount等于k,则匹配到了足够的字符,去掉栈顶的k - 1个元素。
否则将字符串加入栈顶,记录当前位置的出现次数lastCount。

如果当前字符与栈顶字符不同,则将字符直接加入栈顶,记录当前位置的cnt[dq.size()]为1。

最后将栈内元素倒序组成字符串。由于需要这一步操作,因此之前的栈采用双端队列实现。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public String removeDuplicates(String s, int k) {
Deque<Character> dq = new LinkedList<Character>();
int[] cnt = new int[s.length()+1];

for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(i > 0 && !dq.isEmpty() && dq.peek() == c){
int lastCount = cnt[dq.size()];
lastCount++;
if(lastCount == k){
while(lastCount != 1){
dq.removeFirst();
lastCount--;
}
}
else{
dq.offerFirst(c);
cnt[dq.size()] = lastCount;
}
}
else{
dq.offerFirst(c);
cnt[dq.size()] = 1;
}
}

StringBuffer sb = new StringBuffer();
while(!dq.isEmpty()){
sb.append(dq.removeLast());
}
return sb.toString();
}
}
72. Edit Distance

72. Edit Distance

Question

Given two strings word1 and word2, return the minimum number of operations required to convert word1 to word2.

You have the following three operations permitted on a word:

  • Insert a character
  • Delete a character
  • Replace a character

Solution

本题和583. Delete Operation for Two Strings大同小异。

动态规划,两个字符串的分别以长宽组成一个矩阵,记录该位置得编辑距离。
将初始的编辑距离dp[i][0]一行和dp[0][j]一列按照对应的位置i和j填写。

双重遍历矩阵的坐标。
当两个字符相等时,编辑距离等于对角线方向的状态的编辑距离(即两个字符都没有取得上一个状态)。
当两个字符不相等时,编辑距离等于左边,上边(少取一个字符的状态)以及对角线方向(两个字符都不取的状态)的编辑距离中得最小值再加上1。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length(), n = word2.length();
char[] bin1 = word1.toCharArray();
char[] bin2 = word2.toCharArray();
int[][] dp = new int[m+1][n+1];

for(int i = 0; i <= m; i++){
dp[i][0] = i;
}
for(int j = 0; j <= n; j++){
dp[0][j] = j;
}

for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
if(bin1[i-1] == bin2[j-1]){
dp[i][j] = dp[i-1][j-1];
}
else{
dp[i][j] = Math.min(dp[i-1][j-1] + 1, Math.min(dp[i-1][j], dp[i][j-1]) + 1);
}
}
}
return dp[m][n];
}
}

583. Delete Operation for Two Strings

Given two strings word1 and word2, return the minimum number of steps required to make word1 and word2 the same.

In one step, you can delete exactly one character in either string.

动态规划,创建数组dp[][],长宽分别为两个字符串的长度。
在dp中填入子字符串修改成另一个子字符串时需要删除的数量。
如果两个字符相同,则相对于上一个状态不需要增加数量。因此可以直接等于对角线方向上的值。
如果两个字符串不同,则取上方向和左方向中的较小值,然后+1。(由于题目只允许删除操作,不允许修改操作,因此不能从字符串左上角取值。)
最后返回dp中右下角的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length(), n = word2.length();
int[][] dp = new int[m+1][n+1];
char[] bin1 = word1.toCharArray();
char[] bin2 = word2.toCharArray();

for(int i = 0; i <= m; i++){
dp[i][0] = i;
}
for(int j = 0; j <= n; j++){
dp[0][j] = j;
}

for(int i = 1; i <= m; i++){
char k = bin1[i-1];
for(int j = 1; j <= n; j++){
if(k == bin2[j-1]){
dp[i][j] = dp[i-1][j-1];
}
else{
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + 1;
}
}
}
return dp[m][n];
}
}

1143. Longest Common Subsequence

Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.

A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.

For example, “ace” is a subsequence of “abcde”.
A common subsequence of two strings is a subsequence that is common to both strings.

动态规划,dp的长和宽分别为两个字符串的长度。
遍历dp字符串的长和宽。(注意在第一层遍历时先保存当前选择的字符可以提升速度。)
当两个字符串相同时,则可以从上一个状态+1。
注意状态转移方程的上一个状态应该是从对角线方向+1。
当两个字符串不同时,则继续保存之前的最大长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] dp = new int[m+1][n+1];
char[] bin1 = text1.toCharArray();
char[] bin2 = text2.toCharArray();

for(int i = 1; i <= m; i++){
int k = bin1[i-1];
for(int j = 1; j <= n ; j++){
if(k == bin2[j-1]){
dp[i][j] = dp[i-1][j-1]+1;
}
else{
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[m][n];
}
}

139. Word Break

Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.

Note that the same word in the dictionary may be reused multiple times in the segmentation.

动态规划,创建一个数组dp[],长度为字符串长度+1,记录是否可以达到。
将dp[0]设置为1,表示可以达到。

从可取长度为1开始,遍历直到可取长度为字符串的长度。
用下一个遍历j将字符串分为两部分。(这里j从i-1下降到0会比从0上升到i-1更快。)
当dp[j]可以到达,且当前的哈希表中有(i-j)组成的字符串时,则dp[i]也可以到达。结束第二层循环。
继续搜索更长的字符串是否可以达到。
最后返回dp中的最后一个元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> set = new HashSet<>();
int n = s.length();
int[] dp = new int[n+1];

for(String word : wordDict){
set.add(word);
}
dp[0] = 1;
int temp = 0;
for(int i = 1; i <= n; i++){
for(int j = i - 1; j >= 0; j--){
if(dp[j] == 1 && set.contains(s.substring(j, i))){
dp[i] = 1;
break;
}
}
}
return dp[n] == 1;
}
}

1249. Minimum Remove to Make Valid Parentheses

Given a string s of ‘(‘ , ‘)’ and lowercase English characters.

Your task is to remove the minimum number of parentheses ( ‘(‘ or ‘)’, in any positions ) so that the resulting parentheses string is valid and return any valid string.

Formally, a parentheses string is valid if and only if:

  • It is the empty string, contains only lowercase characters, or
  • It can be written as AB (A concatenated with B), where A and B are valid strings, or
  • It can be written as (A), where A is a valid string.

用栈储存括号,按顺序将括号压入栈。
如果和上一个括号配对,则挤出上一个括号。

当栈不为空时,如果栈顶的符号为“)”,则优先去掉字符串左侧的括号。
如果栈顶的符号为“(”,则优先去掉字符串右侧的括号。
最后返回字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public String minRemoveToMakeValid(String s) {
Stack<Character> stack = new Stack<>();
StringBuffer sb = new StringBuffer(s);

for(int i = 0; i < s.length(); i++){
if(s.charAt(i) == '(') stack.push('(');
else if(s.charAt(i) == ')'){
if(!stack.isEmpty() && stack.peek() == '(') stack.pop();
else stack.push(')');
}
}

while(!stack.isEmpty()){
if(stack.pop() == ')'){
int index = sb.indexOf(")");
sb.delete(index, index+1);
}
else{
int index = sb.lastIndexOf("(");
sb.delete(index, index+1);
}
}
return sb.toString();
}
}