Skip to content

Commit fe2d195

Browse files
committed
分割回文字符串
1 parent e6db775 commit fe2d195

File tree

1 file changed

+44
-81
lines changed

1 file changed

+44
-81
lines changed

basic_algorithm/dp.md

Lines changed: 44 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -283,97 +283,60 @@ bool canJump(vector<int>& nums) {
283283
> 数组中的每个元素代表你在该位置可以跳跃的最大长度。
284284
> 你的目标是使用最少的跳跃次数到达数组的最后一个位置。
285285
286-
```go
287-
// v1动态规划(其他语言超时参考v2)
288-
func jump(nums []int) int {
289-
// 状态:f[i] 表示从起点到当前位置最小次数
290-
// 推导:f[i] = f[j],a[j]+j >=i,min(f[j]+1)
291-
// 初始化:f[0] = 0
292-
// 结果:f[n-1]
293-
f := make([]int, len(nums))
294-
f[0] = 0
295-
for i := 1; i < len(nums); i++ {
296-
// f[i] 最大值为i
297-
f[i] = i
298-
// 遍历之前结果取一个最小值+1
299-
for j := 0; j < i; j++ {
300-
if nums[j]+j >= i {
301-
f[i] = min(f[j]+1,f[i])
302-
}
303-
}
304-
}
305-
return f[len(nums)-1]
306-
}
307-
func min(a, b int) int {
308-
if a > b {
309-
return b
310-
}
311-
return a
312-
}
313-
```
314-
315-
```go
316-
// v2 动态规划+贪心优化
317-
func jump(nums []int) int {
318-
n:=len(nums)
319-
f := make([]int, n)
320-
f[0] = 0
321-
for i := 1; i < n; i++ {
322-
// 取第一个能跳到当前位置的点即可
323-
// 因为跳跃次数的结果集是单调递增的,所以贪心思路是正确的
324-
idx:=0
325-
for idx<n&&idx+nums[idx]<i{
326-
idx++
286+
```c++
287+
int jump(vector<int>& nums) {
288+
int size = nums.size();
289+
vector<int> cache(size);
290+
cache[0] = 0;
291+
// cache[i + 1] >= cache[i]
292+
for (int i = 1, farest = 0; i < size; ++i) {
293+
while (farest < size && farest + nums[farest] < i) {
294+
++farest;
327295
}
328-
f[i]=f[idx]+1
296+
// 假设你总是可以到达数组的最后一个位置。
297+
// 可以认定farest + nums[farest]一定会大于i
298+
cache[i] = cache[farest] + 1;
329299
}
330-
return f[n-1]
300+
return cache.back();
331301
}
332-
333302
```
334303
335304
### [palindrome-partitioning-ii](https://leetcode-cn.com/problems/palindrome-partitioning-ii/)
336305
337306
> 给定一个字符串 _s_,将 _s_ 分割成一些子串,使每个子串都是回文串。
338307
> 返回符合要求的最少分割次数。
339308
340-
```go
341-
func minCut(s string) int {
342-
// state: f[i] "前i"个字符组成的子字符串需要最少几次cut(个数-1为索引)
343-
// function: f[i] = MIN{f[j]+1}, j < i && [j+1 ~ i]这一段是一个回文串
344-
// intialize: f[i] = i - 1 (f[0] = -1)
345-
// answer: f[s.length()]
346-
if len(s) == 0 || len(s) == 1 {
347-
return 0
348-
}
349-
f := make([]int, len(s)+1)
350-
f[0] = -1
351-
f[1] = 0
352-
for i := 1; i <= len(s); i++ {
353-
f[i] = i - 1
354-
for j := 0; j < i; j++ {
355-
if isPalindrome(s, j, i-1) {
356-
f[i] = min(f[i], f[j]+1)
357-
}
358-
}
359-
}
360-
return f[len(s)]
361-
}
362-
func min(a, b int) int {
363-
if a > b {
364-
return b
365-
}
366-
return a
367-
}
368-
func isPalindrome(s string, i, j int) bool {
369-
for i < j {
370-
if s[i] != s[j] {
371-
return false
372-
}
373-
i++
374-
j--
375-
}
376-
return true
309+
```c++
310+
int minCut(string s) {
311+
if (s.size() <= 1) {
312+
return 0;
313+
}
314+
auto size = s.size();
315+
vector<int> cache(size);
316+
for (int i = 0; i < size; ++i) {
317+
cache[i] = i;
318+
}
319+
vector<vector<bool>> isPalindrome(size, vector<bool>(size));
320+
for (int right = 0; right < size; ++right) {
321+
for (int left = 0; left <= right; ++left) {
322+
// 如果头尾相同,可能为回文!
323+
// len < 3时,直接判断为回文
324+
// len >= 3时,判断里面一层,左右往里缩一格
325+
isPalindrome[left][right] = s[right] == s[left] && (right - left < 3 || isPalindrome[left + 1][right - 1]);
326+
}
327+
}
328+
for (int i = 1; i < size; ++i) {
329+
if (isPalindrome[0][i]) {
330+
cache[i] = 0;
331+
continue;
332+
}
333+
for (int j = 0; j < i; ++j) {
334+
if (isPalindrome[j + 1][i]) {
335+
cache[i] = min(cache[j] + 1, cache[i]);
336+
}
337+
}
338+
}
339+
return cache.back();
377340
}
378341
```
379342

0 commit comments

Comments
 (0)