Palindrome II

本文介绍了一种算法,该算法通过动态规划的方式解决如何将一个字符串分割成多个回文子串的问题,并且使分割次数达到最少。文章详细解释了算法思路、核心步骤及其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem Statement

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

For example, given s = "aab",  Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.

 

First we need to give some definitions. Then

Define

  • $cut[k]$ to indicate the minimum number of cuts of the first $k$ characters in string $s$, {$s_0, s_1, ..., s_{k-1}$}.

 

  • Thus, the index of cut is one more than index of s, which means the number of minimum cut of {$s_0, ..., s_i$} will be stored at $cut[s_i]$, or $cut[i+1]$. For simplicity, we use the terminology minimum cut of $s_i$ to indicate minimum cut of {$s_0, ..., s_i$}.

 

  • So the problem can be expressed to compute $cut[n]$. 

 

Obviously, the initial $cut[i]$ should be $i - 1$. Because every node itself is a palindrome. Thus $n$ nodes at most need $n-1$ partitions:

for(int i = 0; i < n; ++i)
    cut[i] = i - 1; 

 

At first sight, the assignment $cut[0] = -1$ seems like a garbage value, but later we will explain it's very useful to assign $cut[0]$ to -1 to complete our problem.

 

In order to find some intuitions, let's think about the minimum cut of string {$s_0, s_1, ..., s_i$} with $cut[i+1]$ partitions.

Denote the cut like: {$s_0, s_1, ..., s_i$} = {$P_1, P_2, ..., P_k$} =
{
{$s_0, s_1, ..., s_{n_1}$},
{$s_{n_1+1}, s_{n_1+2}, ..., s_{n_2}$},
{...},
....,
{$s_{n_{k-2}+1}, s_{n_{k-2}+2}, ..., s_{n_{k-1}}$},
{$s_{n_{k-1}+1}, s_{n_{k-1}+2}, ..., s_{n_k}$}
}.

Immediately, we can get

  1. The node $s_i$ must be in $P_k$ with $s_i = s_{n_k}$. And $P_k$ is a palindrome.
  2. Also, consider the node $s_{n_{k-1}}$. We can state that {$P_1, P_2, ..., P_{k-1}$} is also the minimum cut of string {$s_0, s_1, ..., s_{n_{k-1}}$}.
    (Otherwise, we can use {$s_0, s_1, ..., s_{n_{k'}}$}'s minimum cut {$P_1', P_2', ..., P_{k-1}'$} $\cup$ {$P_k$} to get {$s_0, s_1, ..., s_i$}'s smaller cut, which contradicates to {$P_1, P_2, ..., P_k$}'s minimum.)
  3. Based on the above two statements, we also get $k = (k-1) + 1$, which means $cut[s_i] = cut[s_{n_{k-1}}] + 1$, or $cut[i+1] = cut[n_{k-1}+1] + 1$.
  4. The situation that the whole string {$s_0, ..., s_i$} is palindrome, i.e., $P_k =$ {$ s_0, ..., s_i $}, $P_{k-1} = \emptyset$ and $cut[i] = 0$, is also included.
    As the deductions above, $cut[i] = cut[s_{-1}] + 1 = cut[0] + 1 = -1 + 1 = 0$, which matches the result of our problem.
  5. Point 4 also explains that the initial assignment of cut[0] = -1 is useful and meaningful.

From these insights, we can get that for every minimum partition, let's say node $s_i$'s minimum cut, there exists some $j$, where $j \leq i$, such that

$s_i$'s minimum cut =

{$s_0, s_1, ..., s_{j-1}$}'s minimum cut

$\cup$

{$s_j, ..., s_i$}, where {$s_j, ..., s_i$} is a palindrome.

Thus, if we test a palindrome {$s_p, ..., s_q$}, it may be the minimum partition's last part $P_k$. So we may have a chance to update $s_q$'s minimum cut, i.e. updating cut[q+1] by $cut[s_q] = min(cut[s_q], cut[s_{p-1}]+1)$

if(isPalindrome(s, p, q))
    cut[q+1] = max(cut[q+1], cut[p]+1)

 

As every node $s_i$'s $cut[i+1]$ can only be affected by its previous nodes, we can systematically detect palindrome and then update $cut[i+1]$ from the beginning of string s.

for(int point = 0; point < n; ++point){
    //detect palindrome and update cut[i]
    ...
}

 

For every node $s_i$, we consider palindrome centralized at $s_i$. Of course, there may be two cases of palindrome centralized at $s_i$:

  • [$s_{i-j}, ..., s_i, ..., s_{i+j}$] with odd length $2j+1$.

 

  • [$s_{i-(j-1)}, ..., s_i, s_{i+1}, ..., s_{i+j}$] with even length $2j$

We can expand the half length $j$ from zero until it reaches the boundary $s_0$ or $s_{n-1}$. And once the expansion of length stops at some length $j'$, there won't exist palindrome centralized at $i$ with length greater than $j'$.

// odd length
for(int halfLength = 0; point-halfLength >= 0 && point+halfLength < n && s[point-halfLength] == s[point+halfLength])
    cut[point+halfLength+1] = min(cut[point+halfLength+1], cut[point-halfLength]+1);
    
// even length
for(int halfLength = 1; point-halfLength+1 >= 0 && point+halfLength < n && s[point-halfLength+1] == s[point+halfLength])
    cut[point+halfLength+1] = min(cut[point+halfLength+1], cut[point-halfLength+1]+1)

or if we use $i$ denote central point, and $j$ denote half length, we can get a piece of simply code:

for (int i = 0; i < n; ++i){
    //odd length
	for (int j = 0; i-j >= 0 && i+j < n && s[i-j] == s[i+j]; ++j)
	    cut[i+j+1] = min(cut[i+j+1], cut[i-j]+1);

	//even length
	for (int j = 1; i-j+1 >= 0 && i+j < n && s[i-j+1] == s[i+j]; ++j)
        cut[i+j+1] = min(cut[i+j+1], cut[i-j+1]+1);
}

 


The complete code is:

int minCut(string s) {
    int n = s.size();
	if(n <= 1) return 0;

	vector<int> cut(n+1, 0);

	for (int i = 0; i <= n; i++) 
		cut[i] = i-1;

	for (int i = 0; i < n; ++i)
	{
		//odd length, i.e. i is the middle point, [i-j, ..., i, ..., i+j];
		for (int j = 0; i-j >= 0 && i+j < n && s[i-j] == s[i+j]; ++j)
		    cut[i+j+1] = min(cut[i+j+1], cut[i-j]+1);

		//even length, i.e. i is left side's endpoint, [i-(j-1), ..., i, i+1, ..., i+j];
		for (int j = 1; i-j+1 >= 0 && i+j < n && s[i-j+1] == s[i+j]; ++j)
		    cut[i+j+1] = min(cut[i+j+1], cut[i-j+1]+1);
	}

	return cut[n];
}

The running time is about two parts. The first is the assignment of $cut[i]$, $O(n)$.

The second part is divided by two for loop:

  • $O(1 + 2 + 3 + 4 + ... + n/2) = O(n^2)$ 
  • $O(1 + 2 + 3 + 4 + ... + n/2) = O(n^2)$

So the total running time is $O(n^2)$.

And obviously the space is $O(n)$.

转载于:https://www.cnblogs.com/kid551/p/4114989.html

资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 华为移动服务(Huawei Mobile Services,简称 HMS)是一个全面开放的移动服务生态系统,为企业和开发者提供了丰富的工具和 API,助力他们构建、运营和推广应用。其中,HMS Scankit 是华为推出的一款扫描服务 SDK,支持快速集成到安卓应用中,能够提供高效且稳定的二维码和条形码扫描功能,适用于商品扫码、支付验证、信息获取等多种场景。 集成 HMS Scankit SDK 主要包括以下步骤:首先,在项目的 build.gradle 文件中添加 HMS Core 库和 Scankit 依赖;其次,在 AndroidManifest.xml 文件中添加相机访问和互联网访问权限;然后,在应用程序的 onCreate 方法中调用 HmsClient 进行初始化;接着,可以选择自定义扫描界面或使用 Scankit 提供的默认扫描界面;最后,实现 ScanCallback 接口以处理扫描成功和失败的回调。 HMS Scankit 内部集成了开源的 Zxing(Zebra Crossing)库,这是一个功能强大的条码和二维码处理库,提供了解码、生成、解析等多种功能,既可以单独使用,也可以与其他扫描框架结合使用。在 HMS Scankit 中,Zxing 经过优化,以更好地适应华为设备,从而提升扫描性能。 通常,ScanKitDemoGuide 包含了集成 HMS Scankit 的示例代码,涵盖扫描界面的布局、扫描操作的启动和停止以及扫描结果的处理等内容。开发者可以参考这些代码,快速掌握在自己的应用中实现扫码功能的方法。例如,启动扫描的方法如下: 处理扫描结果的回调如下: HMS Scankit 支持所有安卓手机,但在华为设备上能够提供最佳性能和体验,因为它针对华为硬件进行了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值