Skip to content

feat: add solutions to lc problem: No.1745 #4109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 58 additions & 21 deletions solution/1700-1799/1745.Palindrome Partitioning IV/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,26 @@ tags:

<!-- solution:start -->

### 方法一:预处理 + 枚举
### 方法一:动态规划

预处理出字符串 `s` 的所有子串是否为回文串,然后枚举 `s` 的所有分割点,判断是否满足条件
我们定义 $f[i][j]$ 表示字符串 $s$ 的第 $i$ 个字符到第 $j$ 个字符是否为回文串,初始时 $f[i][j] = \textit{true}$

时间复杂度 $O(n^2)$,空间复杂度 $O(n^2)$。其中 $n$ 为字符串 `s` 的长度。
然后我们可以通过以下的状态转移方程来计算 $f[i][j]$:

$$
f[i][j] = \begin{cases}
\textit{true}, & \text{if } s[i] = s[j] \text{ and } (i + 1 = j \text{ or } f[i + 1][j - 1]) \\
\textit{false}, & \text{otherwise}
\end{cases}
$$

由于 $f[i][j]$ 依赖于 $f[i + 1][j - 1]$,因此,我们需要从大到小的顺序枚举 $i$,从小到大的顺序枚举 $j$,这样才能保证当计算 $f[i][j]$ 时 $f[i + 1][j - 1]$ 已经被计算过。

接下来,我们枚举第一个子串的右端点 $i$,第二个子串的右端点 $j$,那么第三个子串的左端点可以枚举的范围为 $[j + 1, n - 1]$,其中 $n$ 是字符串 $s$ 的长度。如果第一个子串 $s[0..i]$、第二个子串 $s[i+1..j]$ 和第三个子串 $s[j+1..n-1]$ 都是回文串,那么我们就找到了一种可行的分割方案,返回 $\textit{true}$。

枚举完所有的分割方案后,如果没有找到符合要求的分割方案,那么返回 $\textit{false}$。

时间复杂度 $O(n^2)$,空间复杂度 $O(n^2)$。其中 $n$ 是字符串 $s$ 的长度。

<!-- tabs:start -->

Expand All @@ -70,13 +85,13 @@ tags:
class Solution:
def checkPartitioning(self, s: str) -> bool:
n = len(s)
g = [[True] * n for _ in range(n)]
f = [[True] * n for _ in range(n)]
for i in range(n - 1, -1, -1):
for j in range(i + 1, n):
g[i][j] = s[i] == s[j] and (i + 1 == j or g[i + 1][j - 1])
f[i][j] = s[i] == s[j] and (i + 1 == j or f[i + 1][j - 1])
for i in range(n - 2):
for j in range(i + 1, n - 1):
if g[0][i] and g[i + 1][j] and g[j + 1][-1]:
if f[0][i] and f[i + 1][j] and f[j + 1][-1]:
return True
return False
```
Expand All @@ -87,18 +102,18 @@ class Solution:
class Solution {
public boolean checkPartitioning(String s) {
int n = s.length();
boolean[][] g = new boolean[n][n];
for (var e : g) {
Arrays.fill(e, true);
boolean[][] f = new boolean[n][n];
for (var g : f) {
Arrays.fill(g, true);
}
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
g[i][j] = s.charAt(i) == s.charAt(j) && (i + 1 == j || g[i + 1][j - 1]);
f[i][j] = s.charAt(i) == s.charAt(j) && (i + 1 == j || f[i + 1][j - 1]);
}
}
for (int i = 0; i < n - 2; ++i) {
for (int j = i + 1; j < n - 1; ++j) {
if (g[0][i] && g[i + 1][j] && g[j + 1][n - 1]) {
if (f[0][i] && f[i + 1][j] && f[j + 1][n - 1]) {
return true;
}
}
Expand All @@ -115,15 +130,15 @@ class Solution {
public:
bool checkPartitioning(string s) {
int n = s.size();
vector<vector<bool>> g(n, vector<bool>(n, true));
vector<vector<bool>> f(n, vector<bool>(n, true));
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
g[i][j] = s[i] == s[j] && (i + 1 == j || g[i + 1][j - 1]);
f[i][j] = s[i] == s[j] && (i + 1 == j || f[i + 1][j - 1]);
}
}
for (int i = 0; i < n - 2; ++i) {
for (int j = i + 1; j < n - 1; ++j) {
if (g[0][i] && g[i + 1][j] && g[j + 1][n - 1]) {
if (f[0][i] && f[i + 1][j] && f[j + 1][n - 1]) {
return true;
}
}
Expand All @@ -138,21 +153,21 @@ public:
```go
func checkPartitioning(s string) bool {
n := len(s)
g := make([][]bool, n)
for i := range g {
g[i] = make([]bool, n)
for j := range g[i] {
g[i][j] = true
f := make([][]bool, n)
for i := range f {
f[i] = make([]bool, n)
for j := range f[i] {
f[i][j] = true
}
}
for i := n - 1; i >= 0; i-- {
for j := i + 1; j < n; j++ {
g[i][j] = s[i] == s[j] && (i+1 == j || g[i+1][j-1])
f[i][j] = s[i] == s[j] && (i+1 == j || f[i+1][j-1])
}
}
for i := 0; i < n-2; i++ {
for j := i + 1; j < n-1; j++ {
if g[0][i] && g[i+1][j] && g[j+1][n-1] {
if f[0][i] && f[i+1][j] && f[j+1][n-1] {
return true
}
}
Expand All @@ -161,6 +176,28 @@ func checkPartitioning(s string) bool {
}
```

#### TypeScript

```ts
function checkPartitioning(s: string): boolean {
const n = s.length;
const f: boolean[][] = Array.from({ length: n }, () => Array(n).fill(true));
for (let i = n - 1; i >= 0; --i) {
for (let j = i + 1; j < n; ++j) {
f[i][j] = s[i] === s[j] && f[i + 1][j - 1];
}
}
for (let i = 0; i < n - 2; ++i) {
for (let j = i + 1; j < n - 1; ++j) {
if (f[0][i] && f[i + 1][j] && f[j + 1][n - 1]) {
return true;
}
}
}
return false;
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
79 changes: 60 additions & 19 deletions solution/1700-1799/1745.Palindrome Partitioning IV/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,26 @@ tags:

<!-- solution:start -->

### Solution 1
### Solution 1: Dynamic Programming

We define $f[i][j]$ to indicate whether the substring of $s$ from the $i$-th character to the $j$-th character is a palindrome, initially $f[i][j] = \textit{true}$.

Then we can calculate $f[i][j]$ using the following state transition equation:

$$
f[i][j] = \begin{cases}
\textit{true}, & \text{if } s[i] = s[j] \text{ and } (i + 1 = j \text{ or } f[i + 1][j - 1]) \\
\textit{false}, & \text{otherwise}
\end{cases}
$$

Since $f[i][j]$ depends on $f[i + 1][j - 1]$, we need to enumerate $i$ from large to small and $j$ from small to large, so that when calculating $f[i][j]$, $f[i + 1][j - 1]$ has already been calculated.

Next, we enumerate the right endpoint $i$ of the first substring and the right endpoint $j$ of the second substring. The left endpoint of the third substring can be enumerated in the range $[j + 1, n - 1]$, where $n$ is the length of the string $s$. If the first substring $s[0..i]$, the second substring $s[i+1..j]$, and the third substring $s[j+1..n-1]$ are all palindromes, then we have found a feasible partitioning scheme and return $\textit{true}$.

After enumerating all partitioning schemes, if no valid partitioning scheme is found, return $\textit{false}$.

Time complexity is $O(n^2)$, and space complexity is $O(n^2)$. Where $n$ is the length of the string $s$.

<!-- tabs:start -->

Expand All @@ -64,13 +83,13 @@ tags:
class Solution:
def checkPartitioning(self, s: str) -> bool:
n = len(s)
g = [[True] * n for _ in range(n)]
f = [[True] * n for _ in range(n)]
for i in range(n - 1, -1, -1):
for j in range(i + 1, n):
g[i][j] = s[i] == s[j] and (i + 1 == j or g[i + 1][j - 1])
f[i][j] = s[i] == s[j] and (i + 1 == j or f[i + 1][j - 1])
for i in range(n - 2):
for j in range(i + 1, n - 1):
if g[0][i] and g[i + 1][j] and g[j + 1][-1]:
if f[0][i] and f[i + 1][j] and f[j + 1][-1]:
return True
return False
```
Expand All @@ -81,18 +100,18 @@ class Solution:
class Solution {
public boolean checkPartitioning(String s) {
int n = s.length();
boolean[][] g = new boolean[n][n];
for (var e : g) {
Arrays.fill(e, true);
boolean[][] f = new boolean[n][n];
for (var g : f) {
Arrays.fill(g, true);
}
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
g[i][j] = s.charAt(i) == s.charAt(j) && (i + 1 == j || g[i + 1][j - 1]);
f[i][j] = s.charAt(i) == s.charAt(j) && (i + 1 == j || f[i + 1][j - 1]);
}
}
for (int i = 0; i < n - 2; ++i) {
for (int j = i + 1; j < n - 1; ++j) {
if (g[0][i] && g[i + 1][j] && g[j + 1][n - 1]) {
if (f[0][i] && f[i + 1][j] && f[j + 1][n - 1]) {
return true;
}
}
Expand All @@ -109,15 +128,15 @@ class Solution {
public:
bool checkPartitioning(string s) {
int n = s.size();
vector<vector<bool>> g(n, vector<bool>(n, true));
vector<vector<bool>> f(n, vector<bool>(n, true));
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
g[i][j] = s[i] == s[j] && (i + 1 == j || g[i + 1][j - 1]);
f[i][j] = s[i] == s[j] && (i + 1 == j || f[i + 1][j - 1]);
}
}
for (int i = 0; i < n - 2; ++i) {
for (int j = i + 1; j < n - 1; ++j) {
if (g[0][i] && g[i + 1][j] && g[j + 1][n - 1]) {
if (f[0][i] && f[i + 1][j] && f[j + 1][n - 1]) {
return true;
}
}
Expand All @@ -132,21 +151,21 @@ public:
```go
func checkPartitioning(s string) bool {
n := len(s)
g := make([][]bool, n)
for i := range g {
g[i] = make([]bool, n)
for j := range g[i] {
g[i][j] = true
f := make([][]bool, n)
for i := range f {
f[i] = make([]bool, n)
for j := range f[i] {
f[i][j] = true
}
}
for i := n - 1; i >= 0; i-- {
for j := i + 1; j < n; j++ {
g[i][j] = s[i] == s[j] && (i+1 == j || g[i+1][j-1])
f[i][j] = s[i] == s[j] && (i+1 == j || f[i+1][j-1])
}
}
for i := 0; i < n-2; i++ {
for j := i + 1; j < n-1; j++ {
if g[0][i] && g[i+1][j] && g[j+1][n-1] {
if f[0][i] && f[i+1][j] && f[j+1][n-1] {
return true
}
}
Expand All @@ -155,6 +174,28 @@ func checkPartitioning(s string) bool {
}
```

#### TypeScript

```ts
function checkPartitioning(s: string): boolean {
const n = s.length;
const f: boolean[][] = Array.from({ length: n }, () => Array(n).fill(true));
for (let i = n - 1; i >= 0; --i) {
for (let j = i + 1; j < n; ++j) {
f[i][j] = s[i] === s[j] && f[i + 1][j - 1];
}
}
for (let i = 0; i < n - 2; ++i) {
for (let j = i + 1; j < n - 1; ++j) {
if (f[0][i] && f[i + 1][j] && f[j + 1][n - 1]) {
return true;
}
}
}
return false;
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ class Solution {
public:
bool checkPartitioning(string s) {
int n = s.size();
vector<vector<bool>> g(n, vector<bool>(n, true));
vector<vector<bool>> f(n, vector<bool>(n, true));
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
g[i][j] = s[i] == s[j] && (i + 1 == j || g[i + 1][j - 1]);
f[i][j] = s[i] == s[j] && (i + 1 == j || f[i + 1][j - 1]);
}
}
for (int i = 0; i < n - 2; ++i) {
for (int j = i + 1; j < n - 1; ++j) {
if (g[0][i] && g[i + 1][j] && g[j + 1][n - 1]) {
if (f[0][i] && f[i + 1][j] && f[j + 1][n - 1]) {
return true;
}
}
}
return false;
}
};
};
16 changes: 8 additions & 8 deletions solution/1700-1799/1745.Palindrome Partitioning IV/Solution.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
func checkPartitioning(s string) bool {
n := len(s)
g := make([][]bool, n)
for i := range g {
g[i] = make([]bool, n)
for j := range g[i] {
g[i][j] = true
f := make([][]bool, n)
for i := range f {
f[i] = make([]bool, n)
for j := range f[i] {
f[i][j] = true
}
}
for i := n - 1; i >= 0; i-- {
for j := i + 1; j < n; j++ {
g[i][j] = s[i] == s[j] && (i+1 == j || g[i+1][j-1])
f[i][j] = s[i] == s[j] && (i+1 == j || f[i+1][j-1])
}
}
for i := 0; i < n-2; i++ {
for j := i + 1; j < n-1; j++ {
if g[0][i] && g[i+1][j] && g[j+1][n-1] {
if f[0][i] && f[i+1][j] && f[j+1][n-1] {
return true
}
}
}
return false
}
}
12 changes: 6 additions & 6 deletions solution/1700-1799/1745.Palindrome Partitioning IV/Solution.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
class Solution {
public boolean checkPartitioning(String s) {
int n = s.length();
boolean[][] g = new boolean[n][n];
for (var e : g) {
Arrays.fill(e, true);
boolean[][] f = new boolean[n][n];
for (var g : f) {
Arrays.fill(g, true);
}
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
g[i][j] = s.charAt(i) == s.charAt(j) && (i + 1 == j || g[i + 1][j - 1]);
f[i][j] = s.charAt(i) == s.charAt(j) && (i + 1 == j || f[i + 1][j - 1]);
}
}
for (int i = 0; i < n - 2; ++i) {
for (int j = i + 1; j < n - 1; ++j) {
if (g[0][i] && g[i + 1][j] && g[j + 1][n - 1]) {
if (f[0][i] && f[i + 1][j] && f[j + 1][n - 1]) {
return true;
}
}
}
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
class Solution:
def checkPartitioning(self, s: str) -> bool:
n = len(s)
g = [[True] * n for _ in range(n)]
f = [[True] * n for _ in range(n)]
for i in range(n - 1, -1, -1):
for j in range(i + 1, n):
g[i][j] = s[i] == s[j] and (i + 1 == j or g[i + 1][j - 1])
f[i][j] = s[i] == s[j] and (i + 1 == j or f[i + 1][j - 1])
for i in range(n - 2):
for j in range(i + 1, n - 1):
if g[0][i] and g[i + 1][j] and g[j + 1][-1]:
if f[0][i] and f[i + 1][j] and f[j + 1][-1]:
return True
return False
Loading